所有轉出博客園,請您注明出處:http://m.survivalescaperooms.com/xiaobajiu/p/4122034.html
objc的單例的兩種安全實現方案
首先應該知道單例的實現有兩大類,一個是懶漢式,一個是餓漢式。所謂的懶漢式就是在我們用到某對象(資源)的時候,去問一個負責提供的方法要那個對象。那個方法發現沒有這個資源時就去創建資源,如果是已經有該資源時就直接返回這個資源。而餓漢式就是那個負責提供的方法早已為我們準備好了我們想要的資源問它,它就提供給我們那個它早就準備好了的資源。
餓漢式的實現是簡單而且好理解,但是它的理念不適合移動設備,因為餓漢式提前占用了內存,卻不管我們需不需要。所以餓漢式的實現在最后作為一點補充。我們主要使用懶漢式,它符合移動的開發理念。
首先理清實現單例模式(懶漢式)的思路:
1.依靠什么來單例?
答:依靠靜態變量,因為它在內存中只有一份。只要判斷這個變量存不存在我們就知道需不需要創建一個對象賦值給那個靜態變量。
2.但是在并發的情況下,我們同時去判斷一個變量都得到了nil的結果,這兩者就會都去創建一個新對象,那么會導致唯一性失效,就會使這兩次訪問得到不同的結果了,怎么解決?
答:加鎖或者使用dispatch_once。
請先看加鎖的方案。多說兩句,加鎖@synchronized()的效率可不是java,c#那么高的。《cocao開發者編程手冊》47頁這樣寫道:“這需要精確的垃圾回收,以及重寫本地二進制文件的能力,Objectic-C編譯器這兩件事都做不了。所以,這個關鍵字非常低效”。但其實我們是可以避免頻繁加鎖,在網上找到的大多數代碼都沒有考慮到頻繁加鎖的問題的,而這里解決了這個問題,直接看代碼吧:
//// God.h#import <Foundation/Foundation.h>@interface God : NSObject<NSCopying>+ (God*)sharedGod;@end
//// God.m#import "God.h"static God *_singleGod;@implementation God+ (id)allocWithZone:(struct _NSZone *)zone{ if(!_singleGod){//防止頻繁加鎖 @synchronized([God class]){ if(!_singleGod) _singleGod= [super allocWithZone:zone]; } } return _singleGod;}+ (God*)sharedGod{ if(!_singleGod){//防止頻繁加鎖 @synchronized([God class]){ if(!_singleGod){ _singleGod= [[self alloc] init]; } } } return _singleGod;}- (id)copyWithZone:(NSZone *)zone{ return _singleGod;}//MRC下增加下面四個方法即可- (id)retain{ return self;}- (NSUInteger)retainCount{ return 1;}- (oneway void)release{}- (id)autorelease{ return self;}@end
加鎖的方案看起來似乎過于臃腫,使用GCD提供的dispatch_once來解決,將會使代碼看起來簡潔有力。(注:dispatch_once是GCD提供的一次性代碼方案,該方法在整個程序的生命周期中只會執行一次。下一次執行到這里不會再執行了。)
dispatch_once版的實現如下:
//// God2.m#import "God2.h"static God2 *singleGod2;@implementation God2+ (id)allocWithZone:(struct _NSZone *)zone{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ singleGod2= [super allocWithZone:zone]; }); return singleGod2;}+ (instancetype)sharedGod2{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ singleGod2= [[self alloc] init]; }); return singleGod2;}- (id)copyWithZone:(NSZone *)zone{ return singleGod2;}//MRC下增加下面四個方法即可- (id)retain{ return self;}- (NSUInteger)retainCount{ return 1;}- (oneway void)release{}- (id)autorelease{ return self;}@end
使用dispatch_once來實現單例,我們更本不需要擔心線程安全問題,因為GCD提供的解決方案本身就是線程安全的。大大簡化了開發難度。
最后簡單的補充一點關于懶漢式的寫法,那么要知道+(void)load這個方法。它是類型加載到runtime時調用該方法,并且僅此一次。它的核心就是下面一行代碼:
+ (void)load{ singleGod3= [[self alloc] init];}
歡迎各位朋友指正,以免誤人子弟。
新聞熱點
疑難解答