最近翻筆記组底,發(fā)現(xiàn)Singleton知識(shí)點(diǎn)漏洞太多艘绍,于是重新整理了一下(友情提示:可以配上 竹仲絵里 的 いつも何度でも 一起欣賞)仆抵。
這是一篇關(guān)于單小姐的故事巩割。
what‘s Singleton?
首先亭珍,我們重新復(fù)習(xí)一下:what‘s Singleton敷钾?
單例模式,通常的理解就是一個(gè)實(shí)例變量只會(huì)為一個(gè)類而存在肄梨,針對(duì)這個(gè)實(shí)例變量有一個(gè)全局的入口阻荒;當(dāng)?shù)谝淮蝿?chuàng)建它的時(shí)候通常會(huì)采用延遲加載的方式,只會(huì)調(diào)用一次众羡,即便后來(lái)它出了意外被銷毀财松,也不會(huì)再重新創(chuàng)建。
單例的使用場(chǎng)景:
- 類只能有一個(gè)實(shí)例纱控,并且必須從一個(gè)為數(shù)值的訪問(wèn)點(diǎn)對(duì)其訪問(wèn)辆毡。
- 這個(gè)唯一的實(shí)例只能通過(guò)子類化進(jìn)行拓展,并且拓展的對(duì)象不會(huì)破壞客戶端代碼甜害。
蘋果提供了很多單例模式的例子舶掖,比如:[NSUserDefaults standerUserDefaults].etc,我們?cè)趯?shí)踐中也經(jīng)常會(huì)用到尔店,比如登錄眨攘,etc.
好了,復(fù)習(xí)一下概念嚣州,具體可以參考:
https://www.raywenderlich.com/46988/ios-design-patterns
NextStep鲫售,the point!
how to use Singleton该肴?
由于我們不知道會(huì)什么時(shí)候情竹、通過(guò)什么樣的方式創(chuàng)建單例,所以我們?cè)趧?chuàng)建Singleton的時(shí)候匀哄,一定要考慮全面秦效,翻閱我之前的筆記,漏洞百出涎嚼,沒(méi)有考慮環(huán)境阱州,沒(méi)有考慮方式,所以如果別人用我寫的單例法梯,可能會(huì)有分分鐘重構(gòu)代碼的沖動(dòng)吧苔货,所以,作為一個(gè)coder monkey 立哑,邏輯思維夜惭,思考方式很重要。
分享一個(gè)應(yīng)該是最完整的單例創(chuàng)建方式:
+(instancetype)sharedSoundTool{
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
//
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}
//
+ (id)copyWithZone:(nullable NSZone *)zone{
return self;
}
//
#pragma mark - 在非ARC環(huán)境下刁憋,還要重寫下面方法
+ (oneway void)release {
}
//
+ (instancetype)autorelease {
return self;
}
//
+ (instancetype)retain {
return self;
}
//
+ (NSUInteger)retainCount {
return 1;
}
考慮的很全面滥嘴,無(wú)論是ARC還是MRC,各種方式至耻,都考慮到了若皱。
很有意思的玩法,一行代碼創(chuàng)建單例:
- 首先尘颓,先抽.h中:
#define Singleton_h(name) +(instancetype)shared##name;
#if __has_feature(objc_arc) // arc環(huán)境
#define Singleton_m(name) +(instancetype)shared##name{ \
static id instance = nil; \
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
instance = [[self alloc] init];\
});\
return instance;\
}\
\
+ (instancetype)allocWithZone:(struct _NSZone *)zone {\
static id instance = nil;\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
instance = [super allocWithZone:zone];\
});\
return instance;\
}\
\
- (id)copyWithZone:(nullable NSZone *)zone{\
return self;\
}
#else // 非arc環(huán)境
#define Singleton_m(name) +(instancetype)shared##name{ \
static id instance = nil; \
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
instance = [[self alloc] init];\
});\
return instance;\
}\
\
+ (instancetype)allocWithZone:(struct _NSZone *)zone {\
static id instance = nil;\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
instance = [super allocWithZone:zone];\
});\
return instance;\
}\
\
+ (id)copyWithZone:(nullable NSZone *)zone{\
return self;\
}\
+ (oneway void)release {\
}\
\
+ (instancetype)autorelease {\
return self;\
}\
\
+ (instancetype)retain {\
return self;\
}\
\
+ (NSUInteger)retainCount {\
return 1;\
}
#endif
然后走触,重點(diǎn)來(lái)了
- 在你想創(chuàng)建單例的.h中
#import "Singleton.h"
@interface HttpTool : NSObject
Singleton_h(HttpTool)
@end
- 接著,在你想創(chuàng)建單例的.m中
@implementation HttpTool
Singleton_m(HttpTool);
@end
Finish疤苹!很爽吧互广?
About thread
單例模式中需要用到dispatch_once,意義很明顯,就是希望dispatch_once中參數(shù)的block在全局中只執(zhí)行一次惫皱,但如果出現(xiàn)下面的情況像樊,也許就有點(diǎn)茫然了。
- (void)foo{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self foo];
});
// whatever...}
是的旅敷,你猜的沒(méi)錯(cuò)生棍,它發(fā)生死鎖了,但為什么會(huì)發(fā)生死鎖呢媳谁?
觀察一下程序的調(diào)用棧涂滴,
它在dispatch_once_f卡住了,查閱了相關(guān)資料晴音,有興趣的同學(xué)可以去看看dispatch_once_f的實(shí)現(xiàn)柔纵,大概就是onceToken在執(zhí)行第一次block的時(shí)候,將其值由NULL指向第一次執(zhí)行它的指針锤躁,在block執(zhí)行完成之前搁料,所有進(jìn)來(lái)的其他的調(diào)用者都被派遣到一個(gè)叫waiter的鏈表里,這些調(diào)用者呢都在等待一個(gè)信號(hào)量进苍,當(dāng)block完成后加缘,oncetoken將被置為DISPATCH_ONCE_DONE,同時(shí)也會(huì)給waiter拋出一個(gè)信號(hào)觉啊,告訴它們我已經(jīng)完成啦拣宏。看到這里杠人,估計(jì)大家明白了勋乾,foo在第二次調(diào)用的時(shí)候,它跑到了waiter鏈表里嗡善,等待block中自己(foo)發(fā)送一個(gè)完成的信號(hào)量辑莫,所以,它發(fā)生了死鎖罩引。
參考資料:http://stackoverflow.com/questions/19176219/why-am-i-getting-deadlock-with-dispatch-once
所以各吨,這個(gè)故事告訴我們 :?jiǎn)卫m好,可不要遞歸調(diào)用哦袁铐。