【轉載】該作者原文鏈接https://juejin.im/post/5cb067676fb9a0688360f987
PS:結尾有作者寫好的宏,嫌長可直接下拉
V1.0
可能有很多人不服氣,單例么, 有什么難的, 一個簡單的 dispatch_once
不就解決了么! 比如下邊的代碼:
@implementation SingletonClass
+ (instancetype)sharedInstance {
static SingletonClass *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
//...
@end
復制代碼
可能大部分人的單例都是這么實現(xiàn)的, 貌似也沒啥問題,通過[SingletonClass sharedInstance]
獲取到的的確都是同一個實例。但是有沒有例外情況呢?
- 比如
SingletonClass
這個類需要托管到其他框架, 那么其他框架生成實例的時候, 為了通用, 一般都會通過[[SingletonClass alloc] init]
來初始化; - 項目中的單例類又沒有明顯標識, 長的和其他類差不多, 那么會不會有某些同事"誤用"
[[SingletonClass alloc] init]
來初始化呢? 畢竟你又沒有規(guī)定不讓用。
那么問題來了, 運行下邊代碼:
NSLog(@"1: %p", [SingletonClass sharedInstance]);
NSLog(@"2: %p", [SingletonClass sharedInstance]);
NSLog(@"3: %p", [[SingletonClass alloc] init]);
NSLog(@"4: %p", [[SingletonClass alloc] init]);
復制代碼
輸出結果:
2019-04-12 18:44:51.147445+0800 TestProj[92371:7344641] 1: 0x600002a0c360
2019-04-12 18:44:51.147553+0800 TestProj[92371:7344641] 2: 0x600002a0c360
2019-04-12 18:44:51.147630+0800 TestProj[92371:7344641] 3: 0x600002a1e700
2019-04-12 18:44:51.147737+0800 TestProj[92371:7344641] 4: 0x600002a11060
復制代碼
可以看出, 1和2是一樣的, 但是和3, 4都不一樣, 所以這種方案不完善。
弊端:沒有保證無論用何種初始化方法, 都應該只有一個實例。
V2.0
在很久很久以前, iOS的蠻荒時代, 那時候還沒有 swift, 蘋果還把 Objective-C 叫“小甜甜”。 在蘋果網(wǎng)站上, 曾經(jīng)有一份OC實現(xiàn)單例的 sample code(現(xiàn)在沒有了,鏈接失效了, 現(xiàn)在只有 swift 的, 畢竟現(xiàn)在的小甜甜是 swift)。 費了老大的勁, 終于從一些別人的歷史文章里邊找到了和當年蘋果差不多的實現(xiàn):
static SingletonClass *sharedInstance = nil;
@implementation SingletonClass
#pragma mark Singleton Methods
+ (id)sharedInstance {
@synchronized(self) {
if(sharedInstance == nil)
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [[self sharedInstance] retain];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; //denotes an object that cannot be released
}
- (oneway void)release {
// never release
}
- (id)autorelease {
return self;
}
- (void)dealloc {
// Should never be called, but just here for clarity really.
[someProperty release];
[super dealloc];
}
@end
復制代碼
這個還是 MRC 的, 那時候也還沒有 dispatch_once
。 改寫成 ARC 之后測試看看:
2019-04-12 21:59:16.844126+0800 TestProj[6248:7514391] 1: 0x600002afc430
2019-04-12 21:59:16.844285+0800 TestProj[6248:7514391] 2: 0x600002afc430
2019-04-12 21:59:16.844402+0800 TestProj[6248:7514391] 3: 0x600002afc430
2019-04-12 21:59:16.844499+0800 TestProj[6248:7514391] 4: 0x600002afc430
復制代碼
OK! 完美!
且慢~~ 在用到項目中的時候, 還是有問題。 原來項目中有單例繼承的情況??(關于單例是否可以繼承, 以及什么場景下用單例繼承, 這是另外一個爭論話題~)。 那就寫個子類繼承單例, 測試一下吧:
@interface SingletonClassSon : SingletonClass
@end
@implementation SingletonClassSon
@end
/// test case:
NSLog(@"01: %@", [SingletonClass sharedInstance]);
NSLog(@"02: %@", [[SingletonClass alloc] init]);
NSLog(@"11: %@", [SingletonClassSon sharedInstance]);
NSLog(@"12: %@", [[SingletonClassSon alloc] init]);
復制代碼
運行結果如下:
2019-04-12 22:10:47.305874+0800 TestProj[6737:7524929] 01: <SingletonClass: 0x60000166ca20>
2019-04-12 22:10:47.306011+0800 TestProj[6737:7524929] 02: <SingletonClass: 0x60000166ca20>
2019-04-12 22:10:47.306110+0800 TestProj[6737:7524929] 11: <SingletonClass: 0x60000166ca20>
2019-04-12 22:10:47.306191+0800 TestProj[6737:7524929] 12: <SingletonClass: 0x60000166ca20>
復制代碼
WTF?爹還是爹, 兒子不見了? 原因是子類調用的是父類的sharedInstance
方法, 直接返回父類的實例了, 子類根本沒有被 alloc!
修改一下, 給兒子把方法補全:
@interface SingletonClassSon : SingletonClass
@end
@implementation SingletonClassSon
#pragma mark Singleton Methods
+ (id)sharedInstance {
static SingletonClassSon *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] init];
});
return sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
@end
復制代碼
繼續(xù)運行原來的 test case, 崩了:
[圖片上傳失敗...(image-932bb4-1555983303242)]
<figcaption></figcaption>
調用棧如下, 很明顯子類的 sharedInstance
方法發(fā)生了遞歸調用, 導致dispatch_once
死鎖了:[圖片上傳失敗...(image-311d48-1555983303242)]
<figcaption></figcaption>
弊端:無法實現(xiàn)單例繼承
V3.0
仔細觀察上個版本的崩潰堆棧, 發(fā)現(xiàn)問題所在就是 allocWithZone:
的實現(xiàn)! 把兩個類的allocWithZone:
修改如下:
/// 父類
+ (id)allocWithZone:(NSZone *)zone {
if (self == SingletonClass.class) {
return [self sharedInstance];
}
return [super allocWithZone:zone];
}
/// 子類
+ (id)allocWithZone:(NSZone *)zone {
if (self == SingletonClassSon.class) {
return [self sharedInstance];
}
return [super allocWithZone:zone];
}
復制代碼
執(zhí)行測試用例:
2019-04-12 22:46:44.697281+0800 TestProj[8125:7555154] 01: <SingletonClass: 0x6000014b7830>
2019-04-12 22:46:44.697575+0800 TestProj[8125:7555154] 02: <SingletonClass: 0x6000014b7830>
2019-04-12 22:46:44.698047+0800 TestProj[8125:7555154] 11: <SingletonClassSon: 0x6000014b7840>
2019-04-12 22:46:44.698309+0800 TestProj[8125:7555154] 12: <SingletonClassSon: 0x6000014b7840>
復制代碼
?????? 大功告成~~~
放到項目中跑起來, 貌似隱約感覺不對~~~ 有些單例中的狀態(tài)怎么被reset 了? 添加一些生命周期方法, 加上日志測試。。。 原來問題在-init
上!
分別給父類和子類添加如下 -init
方法:
- (instancetype)init {
self = [super init];
NSLog(@"%@ call %s", self, __PRETTY_FUNCTION__);
return self;
}
復制代碼
繼續(xù)運行測試用例, 輸出如下:
2019-04-12 22:46:44.697151+0800 TestProj[8125:7555154] <SingletonClass: 0x6000014b7830> call -[SingletonClass init]
2019-04-12 22:46:44.697281+0800 TestProj[8125:7555154] 01: <SingletonClass: 0x6000014b7830>
2019-04-12 22:46:44.697398+0800 TestProj[8125:7555154] <SingletonClass: 0x6000014b7830> call -[SingletonClass init]
2019-04-12 22:46:44.697575+0800 TestProj[8125:7555154] 02: <SingletonClass: 0x6000014b7830>
2019-04-12 22:46:44.697881+0800 TestProj[8125:7555154] <SingletonClassSon: 0x6000014b7840> call -[SingletonClass init]
2019-04-12 22:46:44.697959+0800 TestProj[8125:7555154] <SingletonClassSon: 0x6000014b7840> call -[SingletonClassSon init]
2019-04-12 22:46:44.698047+0800 TestProj[8125:7555154] 11: <SingletonClassSon: 0x6000014b7840>
2019-04-12 22:46:44.698138+0800 TestProj[8125:7555154] <SingletonClassSon: 0x6000014b7840> call -[SingletonClass init]
2019-04-12 22:46:44.698213+0800 TestProj[8125:7555154] <SingletonClassSon: 0x6000014b7840> call -[SingletonClassSon init]
2019-04-12 22:46:44.698309+0800 TestProj[8125:7555154] 12: <SingletonClassSon: 0x6000014b7840>
復制代碼
一眼就能看到, 只要用 alloc
+ init
的方式獲取單例實例, -init
方法都會被執(zhí)行一次, 單例中的狀態(tài)也就會丟失了~。
弊端:無法保證初始化方法不可重入。
V4.0
我們在項目中, 為了減少重復代碼, 把單例的實現(xiàn)寫成一個模板, 只需要把這個宏添加到類實現(xiàn)中, 就能把這個類變成單例。詳情可以參考我很久很久以前的文章。
如何保證初始化方法不可重入呢? 這個問題我想了好久, 貌似除了在-init
方法中添加初始化標記, 沒有其他辦法了。 但是如何在 -init
中添加標記呢? 我能想到的辦法有倆:
- 通過 method swizzle 替換單例的
-init
方法。 我們可以給每個單例增加一個 category, 然后在 category 中實現(xiàn)+load
方法(不用擔心會覆蓋主類中的+load
, 每個 category 都可以添加自己的+load
方法, 而且這些+load
方法都會被執(zhí)行), 在這里替換掉-init
。 - 模板中實現(xiàn)
-init
, 就可以增加這個標記了, 然后定義一個新的初始化方法-singletonInit
, 在-init
中調用就可以了。外部單例類只需要實現(xiàn)這個-singletonInit
就可以了。
經(jīng)過仔細考慮, 我最后選擇了方案二, 主要是 method swizzle 風險不太可控, 方案二雖然保守, 但是比較可靠。
修改一下單例 -init
方法實現(xiàn):
// 父類, 子類也類似
static SingletonClass *instance_SingletonClass = nil;
- (instancetype)init {
static dispatch_semaphore_t semaphore;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
semaphore = dispatch_semaphore_create(1);
});
SingletonClass *strongRef = instance_SingletonClass;
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (strongRef.class != self.class) {
self = [super init];
if (self.class == SingletonClass.class) {
SEL sel = NSSelectorFromString(@"singletonInit");
if ([self respondsToSelector:sel]) {
[self performSelector:sel];
}
instance_SingletonClass = self;
}
}
dispatch_semaphore_signal(semaphore);
return self;
}
- (void)singletonInit {
NSLog(@"caller: %@; SingletonClass customic init", self);
}
復制代碼
繼續(xù)運行測試用例, 結果如下:
2019-04-13 13:04:35.396087+0800 TestProj[11692:7647465] caller: <SingletonClass: 0x600002c681d0>; SingletonClass customic init
2019-04-13 13:04:35.396231+0800 TestProj[11692:7647465] 01: <SingletonClass: 0x600002c681d0>
2019-04-13 13:04:35.396312+0800 TestProj[11692:7647465] 02: <SingletonClass: 0x600002c681d0>
2019-04-13 13:04:35.396402+0800 TestProj[11692:7647465] caller: <SingletonClassSon: 0x600002c63280>; SingletonClassSon customic init
2019-04-13 13:04:35.396473+0800 TestProj[11692:7647465] 11: <SingletonClassSon: 0x600002c63280>
2019-04-13 13:04:35.396561+0800 TestProj[11692:7647465] 12: <SingletonClassSon: 0x600002c63280>
復制代碼
這次好像沒問題了, 不會重復執(zhí)行-init
方法了。 可是子類的初始化貌似不太對?因為我們現(xiàn)在修改了-init
方法, 真正的類的初始化是在-init
里的-singletonInit
里邊進行的, 因此子類的初始化也必須調用父類的方法, 這樣才能保證完全初始化。 所以我們必須在-singletonInit
中調用 super 方法。 可是問題來了, -singletonInit
是需要開發(fā)者自己實現(xiàn)的, 怎么保證開發(fā)者不會忘記添加[super singletonInit]
呢? 大家可能會想起, 在 xcode 中寫 viewController 的時候,-viewWillAppear:
等方法, 如果不寫 supper 調用, 就會出編譯警告, 提示你必須調用 super 方法。 這就是利用了 llvm 的編譯屬性, 蘋果已經(jīng)把它封裝成一個宏:NS_REQUIRES_SUPER
。 所以我們繼續(xù)添加如下代碼:
/// .h
@interface NSObject (SingletonInit)
- (void)singletonInit NS_REQUIRES_SUPER;
@end
/// .m
@implementation NSObject (SingletonInit)
- (void)singletonInit {}
@end
復制代碼
然后在每個單例的 -singletonInit
中添加[super singletonInit];
, 運行測試用例, 輸出如下:
2019-04-13 13:40:57.294312+0800 TestProj[12932:7675173] caller: <SingletonClass: 0x6000028874f0>; SingletonClass customic init
2019-04-13 13:40:57.294442+0800 TestProj[12932:7675173] 01: <SingletonClass: 0x6000028874f0>
2019-04-13 13:40:57.294569+0800 TestProj[12932:7675173] 02: <SingletonClass: 0x6000028874f0>
2019-04-13 13:40:57.294653+0800 TestProj[12932:7675173] caller: <SingletonClassSon: 0x600002898240>; SingletonClass customic init
2019-04-13 13:40:57.294724+0800 TestProj[12932:7675173] caller: <SingletonClassSon: 0x600002898240>; SingletonClassSon customic init
2019-04-13 13:40:57.294810+0800 TestProj[12932:7675173] 11: <SingletonClassSon: 0x600002898240>
2019-04-13 13:40:57.294879+0800 TestProj[12932:7675173] 12: <SingletonClassSon: 0x600002898240>
復制代碼
事情貌似都解決了, 嗯~~ 好像又看到了一個新概念weak singleton
。 修改成 wean 單例模式:
// static SingletonClass *instance_SingletonClass = nil;
static __weak SingletonClass *instance_SingletonClass = nil;
復制代碼
運行下邊的測試用例:
id obj = [SingletonClass sharedInstance];
NSLog(@"01: %@", obj);
NSLog(@"02: %@", [[SingletonClass alloc] init]);
obj = [SingletonClass sharedInstance];
NSLog(@"11: %@", obj);
NSLog(@"12: %@", [[SingletonClass alloc] init]);
obj = nil;
obj = [SingletonClass sharedInstance];
NSLog(@"21: %@", obj);
NSLog(@"22: %@", [[SingletonClass alloc] init]);
復制代碼
結果如下:
2019-04-14 13:24:21.327596+0800 TestProj[36068:8203530] 01: <SingletonClass: 0x600002c8b2b0>
2019-04-14 13:24:21.327725+0800 TestProj[36068:8203530] 02: <SingletonClass: 0x600002c8b2b0>
2019-04-14 13:24:21.327950+0800 TestProj[36068:8203530] 11: <SingletonClass: 0x600002c8b2b0>
2019-04-14 13:24:21.328037+0800 TestProj[36068:8203530] 12: <SingletonClass: 0x600002c8b2b0>
2019-04-14 13:24:21.328366+0800 TestProj[36068:8203530] 21: (null)
2019-04-14 13:24:21.328617+0800 TestProj[36068:8203530] 22: (null)
復制代碼
對象被釋放之后, 再也不能繼續(xù)創(chuàng)建單例了, 得到的都是nil
。 原因就是, dispatch_once
, 得換個方法。
弊端:不支持 weak 單例
作者:AlexLee002
鏈接:https://juejin.im/post/5cb067676fb9a0688360f987
來源:掘金
著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處。
V5.0
我們把+sharedInstance里邊的dispatch_once換成dispatch_semaphore:
- (id)sharedInstance {
__block SingletonClass *strongRef = instance_SingletonClass;
if (strongRef == nil) {
static dispatch_semaphore_t lock;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
if (instance_SingletonClass == nil) {
strongRef = [[super allocWithZone:NULL] init];
instance_SingletonClass = strongRef;
} else {
strongRef = instance_SingletonClass;
}
dispatch_semaphore_signal(lock);
}
return strongRef;
}
復制代碼輸出如下:
2019-04-14 13:29:20.280302+0800 TestProj[36272:8208680] 01: <SingletonClass: 0x600003824970>
2019-04-14 13:29:20.280400+0800 TestProj[36272:8208680] 02: <SingletonClass: 0x600003824970>
2019-04-14 13:29:20.280486+0800 TestProj[36272:8208680] 11: <SingletonClass: 0x600003824970>
2019-04-14 13:29:20.280594+0800 TestProj[36272:8208680] 12: <SingletonClass: 0x600003824970>
2019-04-14 13:29:20.280871+0800 TestProj[36272:8208680] 21: <SingletonClass: 0x600003824970>
2019-04-14 13:29:20.281358+0800 TestProj[36272:8208680] 22: <SingletonClass: 0x600003824970>
復制代碼至此, 我們得到了一個基本完整ObjC單例實現(xiàn), 我們用宏把它變成一個模板:
ALSingletonTemplate.h
ifndef ALSingletonTemplate_H
define ALSingletonTemplate_H
/**
A template code for define a singleton class.
-
Example:
<code>
// .h file
@interface SingletionTest : NSObject
AS_SINGLETON
@end// .m file
@implementation SingletionTest
SYNTHESIZE_SINGLETON(SingletionTest)
// IMPORTANT: DO NOT add-init
in you singleton class!!! you should use-singletonInit
instead!!!
// and DONT FORGET to add[super singletonInit]
in you singletonInit method.- (void)singletonInit {
[super singletonInit];
// your init code here ...
}
// your code here ...
@end// usage:
SingletionTest *singleton = [SingletionTest sharedInstance];
// or: SingletionTest *singleton = [[SingletionTest alloc] init];
// or: SingletionTest *singleton = [SingletionTest new];
</code>
*/ - (void)singletonInit {
///////////////////////////////////////////////////////////////////////////////////////////////
/// singleton
undef AL_AS_SINGLETON
if __has_feature(objc_arc)
#define AL_AS_SINGLETON \
+ (instancetype)sharedInstance; \
+ (void)al_destroySingleton; \
- (void)al_destroySingleton;
else
#define AL_AS_SINGLETON \
+ (instancetype)sharedInstance;
endif
/// weak singleton; only supports ARC
if __has_feature(objc_arc)
#undef AL_AS_WEAK_SINGLETON
#define AL_AS_WEAK_SINGLETON AL_AS_SINGLETON
endif
///////////////////////////////////////////////////////////////////////////////////////////////
undef AL_SYNTHESIZE_SINGLETON
if __has_feature(objc_arc)
#undef AL_SYNTHESIZE_WEAK_SINGLETON
#define AL_SYNTHESIZE_WEAK_SINGLETON(CLS) \
static __weak CLS *__AL_SINGLETON_INSTANCE_FOR_CLASS(CLS) = nil; \
__AL_SYNTHESIZE_SINGLETON_ARC(CLS);
#define AL_SYNTHESIZE_SINGLETON(CLS) \
static CLS *__AL_SINGLETON_INSTANCE_FOR_CLASS(CLS) = nil; \
__AL_SYNTHESIZE_SINGLETON_ARC(CLS);
else
#define AL_SYNTHESIZE_SINGLETON(CLS) \
static CLS *__AL_SINGLETON_INSTANCE_FOR_CLASS(CLS) = nil; \
__AL_SYNTHESIZE_SINGLETON_MRC(CLS);
endif
///////////////////////////////////////////////////////////////////////////////////////////////
undef __AL_SINGLETON_SEMAPHORE_FOR_CLASS
define __AL_SINGLETON_SEMAPHORE_FOR_CLASS(cls) __AL_SINGLETON_MACRO_CONCAT(_al_singleton_semaphore, cls)
undef __AL_SYNTHESIZE_SINGLETON_COMMON
define __AL_SYNTHESIZE_SINGLETON_COMMON(cls) \
+(dispatch_semaphore_t) __AL_SINGLETON_SEMAPHORE_FOR_CLASS(cls) { \
static dispatch_semaphore_t semaphore; \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
semaphore = dispatch_semaphore_create(1); \
}); \
return semaphore; \
} \
\
+(instancetype) sharedInstance { \
if (self != cls.class) { \
printf( \
"?? [SINGLETON] class `%s` invokes `%s` will return the instance of `%s`, which is not the one " \
"you expected.\n\n", \
NSStringFromClass(self).UTF8String, __PRETTY_FUNCTION__, #cls); \
} \
__block cls *strongRef = __AL_SINGLETON_INSTANCE_FOR_CLASS(cls); \
if (strongRef == nil) { \
dispatch_semaphore_t semaphore = [cls __AL_SINGLETON_SEMAPHORE_FOR_CLASS(cls)]; \
__AL_SINGLETON_SEMAPHORE_WITH_TIMEOUT(semaphore, \
if (__AL_SINGLETON_INSTANCE_FOR_CLASS(cls) == nil) { \
strongRef = [[super allocWithZone:NULL] init]; \
__AL_SINGLETON_INSTANCE_FOR_CLASS(cls) = strongRef; \
} else { strongRef = __AL_SINGLETON_INSTANCE_FOR_CLASS(cls); }); \
} \
return strongRef; \
} \
\
+ (id) allocWithZone : (NSZone *) zone { \
if (self == cls.class) { \
return [self sharedInstance]; \
} \
return [super allocWithZone:zone]; \
} \
\
-(instancetype) init { \
static dispatch_semaphore_t semaphore; \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
semaphore = dispatch_semaphore_create(1); \
}); \
\
cls *strongRef = __AL_SINGLETON_INSTANCE_FOR_CLASS(cls); \
__AL_SINGLETON_SEMAPHORE_WITH_TIMEOUT(semaphore, if (strongRef.class != self.class) { \
self = [super init]; \
if (self.class == cls.class) { \
[self singletonInit]; \
} \
}); \
return self; \
} \
\
-(id) copyWithZone : (nullable NSZone *) zone { \
return self; \
} \
-(id) mutableCopyWithZone : (nullable NSZone *) zone { \
return self; \
}
///////////////////////////////////////////////////////////////////////////////////////////////
undef __AL_SYNTHESIZE_SINGLETON_ARC
define __AL_SYNTHESIZE_SINGLETON_ARC(cls) \
__AL_SYNTHESIZE_SINGLETON_COMMON(cls); \
+ (void)al_destroySingleton { \
printf("?? [SINGLETON] The singleton instance `%s` will be deallocated.\n", \
[self description].UTF8String); \
dispatch_semaphore_t lock = [cls __AL_SINGLETON_SEMAPHORE_FOR_CLASS(cls)]; \
__AL_SINGLETON_SEMAPHORE_WITH_TIMEOUT(lock, \
__AL_SINGLETON_INSTANCE_FOR_CLASS(cls) = nil; \
); \
} \
-(void) al_destroySingleton { \
[self.class al_destroySingleton]; \
};
///////////////////////////////////////////////////////////////////////////////////////////////
undef __AL_SYNTHESIZE_SINGLETON_MRC
define __AL_SYNTHESIZE_SINGLETON_MRC(cls) \
__AL_SYNTHESIZE_SINGLETON_COMMON(cls); \
\
- (instancetype)retain { return self; } \
- (oneway void)release{} \
- (instancetype)autorelease { return self; } \
- (NSUInteger)retainCount { return NSUIntegerMax; }
///////////////////////////////////////////////////////////////////////////////////////////////
undef _AL_SINGLETON_MACRO_CONCAT
define _AL_SINGLETON_MACRO_CONCAT(a, b) a##b
undef __AL_SINGLETON_MACRO_CONCAT
define __AL_SINGLETON_MACRO_CONCAT(a, b) _AL_SINGLETON_MACRO_CONCAT(a, b)
undef __AL_SINGLETON_INSTANCE_FOR_CLASS
define __AL_SINGLETON_INSTANCE_FOR_CLASS(cls) __AL_SINGLETON_MACRO_CONCAT(_al_singleton_instance, cls)
///
/// execute the code statements jobStmt
in dispatch_semaphore.
/// Try to get the semaphore in 10 secods, if failed, that may means a deadlock is occured. and you should check you code.
/// @note DO NOT return in jobStmt
, otherwise the samaphore will not be processed correctly.
///
undef __AL_SINGLETON_SEMAPHORE_WITH_TIMEOUT
define __AL_SINGLETON_SEMAPHORE_WITH_TIMEOUT(sema, jobStmt) \
if (dispatch_semaphore_wait((sema), dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.f * NSEC_PER_SEC))) == 0) { \
jobStmt; \
dispatch_semaphore_signal((sema)); \
} else { \
NSAssert(NO, @"[SINGLETON] %s: timeout while waiting to acquire the lock. Deadlock may occured!", __PRETTY_FUNCTION__); \
}
endif // ALSingletonTemplate_H
復制代碼
NSObject+ALSingletonInit.h@interface NSObject (ALSingletonInit)
- (void)singletonInit NS_REQUIRES_SUPER;
@end
復制代碼
NSObject+ALSingletonInit.m#import "NSObject+ALSingletonInit.h"
@implementation NSObject (ALSingletonInit)
- (void)singletonInit {};
@end
復制代碼
把這幾個文件添加到工程中, 如果某個類需要時單例, 只需在文件中簡單的添加兩行就可以:
// .h
@interface MyClass : NSObject
AL_AS_SINGLETON; // <- 頭文件中加入這個宏
/// your code here ...
@end
// .m
@implementation MyClass
AL_SYNTHESIZE_SINGLETON(MyClass); // <- .m文件中加入這個宏
/// 需要注意的是, 初始化不能直接用 init 方法, 需要用 singletonInit
/// - (void)singletonInit {
/// /// 初始化代碼寫這里, 比如
/// _myIvar = xxx;
/// }
/// your code here ...
@end
復制代碼總結
要用 ObjC 實現(xiàn)一個完整的單例, 需要注意以下幾點:
不管用何種初始化方式, 都只能有一個實例。
alloc init必須保證“原子性”,否則在多線程情況下就會出現(xiàn) ThreadA 執(zhí)行完 alloc, 然后另外一個線程就有可能獲取到的是這個剛 alloc 出來還沒執(zhí)行 init 的實例,導致意外情況。
int 必須保證只能執(zhí)行一次。
【可選】繼承,weak 單例模式還需要另外考慮。
作者:AlexLee002
鏈接:https://juejin.im/post/5cb067676fb9a0688360f987
來源:掘金
著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處。