IOS開發模式-單例模式

jopen 8年前發布 | 10K 次閱讀 單例模式 iOS開發 移動開發

正文

其實小編在接觸了蘋果的新的開發語言swift之后,深刻覺得單例模式在swift里面體現的淋漓盡致,不過在OC里面確實會有一些麻煩。簡單對比一下OC的單例模式和swift的單例模式吧。拿小編之前的一個程序開刀。

struct DeviceData {
    static let height = UIScreen.mainScreen().bounds.height
    static let width  = UIScreen.mainScreen().bounds.width
    static let bound  = UIScreen.mainScreen().bounds
}

再來看一下oc版本的單例

@implementation HMMusicTool
static id _instance;

/**
 *  alloc方法內部會調用這個方法
 */
+ (id)allocWithZone:(struct _NSZone *)zone
{
    if (_instance == nil) { // 防止頻繁加鎖
        @synchronized(self) {
            if (_instance == nil) { // 防止創建多次
                _instance = [super allocWithZone:zone];
            }
        }
    }
    return _instance;
}

+ (instancetype)sharedMusicTool
{
    if (_instance == nil) { // 防止頻繁加鎖
        @synchronized(self) {
            if (_instance == nil) { // 防止創建多次
                _instance = [[self alloc] init];
            }
        }
    }
    return _instance;
}

- (id)copyWithZone:(NSZone *)zone
{
    return _instance;
}
@end

這時候swift的優勢可見一斑吧。不過小編還是會辯證的去看待問題的,swift還是存在一些兼容性的問題的。這次小編先拿oc開刀。底層實現都是一樣的。

簡單介紹單例模式 防止大家百度,小編直接搬來了爛大街的介紹

單例模式的意思就是只有一個實例。單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例。這個類稱為單例類。

1.單例模式的要點:

顯然單例模式的要點有三個;一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。

2.單例模式的優點:

1.實例控制:Singleton 會阻止其他對象實例化其自己的 Singleton 對象的副本,從而確保所有對象都訪問唯一實例。2.靈活性:因為類控制了實例化過程,所以類可以更加靈活修改實例化過程

那么如何優雅的實現oc的單例模式呢。

ARC + 互斥鎖版本

第一步。 為單例對象實現一個靜態實例,并初始化,然后設置成nil

第二步。檢查靜態實例的值是否為nil

第三步。重寫 allocWithZone, copyWithZone 。

第四步。加鎖

代碼如下:

+ (id)allocWithZone:(struct _NSZone *)zone
{
    if (_instance == nil) { // 防止頻繁加鎖
        @synchronized(self) {
            if (_instance == nil) { // 防止創建多次
                _instance = [super allocWithZone:zone];
            }
        }
    }
    return _instance;
}

+ (instancetype)sharedMusicTool
{
    if (_instance == nil) { // 防止頻繁加鎖
        @synchronized(self) {
            if (_instance == nil) { // 防止創建多次
                _instance = [[self alloc] init];
            }
        }
    }
    return _instance;
}

- (id)copyWithZone:(NSZone *)zone
{
    return _instance;
}
@end

調用

HMMusicTool *tool = [[HMMusicTool alloc] init];
 HMMusicTool *tool2 = [[HMMusicTool alloc] init];
 HMMusicTool *tool3 = [HMMusicTool sharedMusicTool];
 HMMusicTool *tool4 = [HMMusicTool sharedMusicTool];

tool。tool2.tool3.tool4的地址是一致的。簡單分析一下上面的程序。類HMMusicTool在alloc的時候會調用 allocWithZone方法。所以重寫 allocWithZone方法。

@synchronized(self) {
            if (_instance == nil) { // 防止創建多次
                _instance = [super allocWithZone:zone];
            }
}

判斷是否創建多次。然后加一個互斥鎖,防止在多線程中創建多個對象,返回對象_instance.

除了通過alloc創建之外,還可以通過類方法創建,典型的創建方法例如  [ UIApplication sharedApplication ]然后我們創建一個類方法。

+ (instancetype)sharedMusicTool
{
    if (_instance == nil) { // 防止頻繁加鎖
        @synchronized(self) {
            if (_instance == nil) { // 防止創建多次
                _instance = [[self alloc] init];//如果第一次使用。調用allocWithZone。
            }
        }
    }
    return _instance;
}

當然除了以上調用方式外。還有可能通過copy實現對象的復制。還需要重寫copy方法。這時候需要遵循NSCoping協議。

- (id)copyWithZone:(NSZone *)zone
{
    return _instance;
}

當調用copyWithZone的時候,往往已經實例化了一個變量。這時候_instance是存在的。所以直接返回即可。

為什么_instance要加上static呢。

_instance作為全局變量。如果不加上static。則在程序的其他文件里面可以通過extern訪問。對_instance的值進行更改,這一點是非常危險的。但是加上static。_instance只能在當前文件訪問。其他文件訪問出錯。

ARC + GCD版本

相對互斥鎖版本沒有相差太多,只是使用了GCD的一個方法。

static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        <#code to be executed once#>
    });

閉包里面的代碼只能被執行一次。所以更改后的程序為

// 用來保存唯一的單例對象
static id _instace;

+ (id)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instace = [super allocWithZone:zone];
    });
    return _instace;
}

+ (instancetype)sharedDataTool
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instace = [[self alloc] init];
    });
    return _instace;


}

- (id)copyWithZone:(NSZone *)zone
{
    return _instace;
}

@end

非ARC版本 單例模式

非ARC版本需要手動管理內存,所以單例模式需要防止單例被釋放,這時候需要重寫幾個方法

- (oneway void)release { }
- (id)retain { return self; }
- (NSUInteger)retainCount { return 1;}
- (id)autorelease { return self;}

這樣子就防止了單例被釋放,保持單例的計數為1。全部代碼為GCD+非ARC

static id _instace;

+ (id)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instace = [super allocWithZone:zone];
    });
    return _instace;
}

+ (instancetype)sharedDataTool
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instace = [[self alloc] init];
    });
    return _instace;
}

- (id)copyWithZone:(NSZone *)zone
{
    return _instace;
}

- (oneway void)release { }
- (id)retain { return self; }
- (NSUInteger)retainCount { return 1;}
- (id)autorelease { return self;}

總結

單例在程序開發當中,有著舉足輕重的地位。當有多個單例對象需要創建的時候,可以考慮把單例抽出來作為宏,不過相對來說調試來說確實會比較坑。不過一切為了簡單嘛。如下

// .h文件
#define HMSingletonH(name) + (instancetype)shared##name;

// .m文件
#define HMSingletonM(name) \
static id _instance; \
 \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        _instance = [super allocWithZone:zone]; \
    }); \
    return _instance; \
} \
 \
+ (instancetype)shared##name \
{ \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        _instance = [[self alloc] init]; \
    }); \
    return _instance; \
} \
 \
- (id)copyWithZone:(NSZone *)zone \
{ \
    return _instance; \
}

來自: http://www.goofyy.com/blog/ios開發模式-單例模式/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!