单例多是 iOS 开发者最熟悉设计模式之一了。 咱们的项目里头也使用了不少单例😓。 最近为了解决项目中单例的 bug 而花费了两天多的时间,发现用 ObjC 写好一个单例真的不容易!swift
可能有不少人不服气,单例么, 有什么难的, 一个简单的 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
复制代码
可能大部分人的单例都是这么实现的, 貌似也没啥问题,经过[SingletonClass sharedInstance]
获取到的的确都是同一个实例。可是有没有例外状况呢?xcode
SingletonClass
这个类须要托管到其余框架, 那么其余框架生成实例的时候, 为了通用, 通常都会经过[[SingletonClass alloc] init]
来初始化;[[SingletonClass alloc] init]
来初始化呢? 毕竟你又没有规定不让用。那么问题来了, 运行下边代码:bash
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都不同, 因此这种方案不完善。框架
弊端:没有保证不管用何种初始化方法, 都应该只有一个实例。测试
在好久好久之前, iOS的蛮荒时代, 那时候尚未 swift, 苹果还把 Objective-C 叫“小甜甜”。 在苹果网站上, 曾经有一份OC实现单例的 sample code(如今没有了,连接失效了, 如今只有 swift 的, 毕竟如今的小甜甜是 swift)。 费了老大的劲, 终于从一些别人的历史文章里边找到了和当年苹果差很少的实现:网站
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 以后测试看看:ui
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! 完美!spa
且慢~~ 在用到项目中的时候, 仍是有问题。 原来项目中有单例继承的状况😭(关于单例是否能够继承, 以及什么场景下用单例继承, 这是另一个争论话题~)。 那就写个子类继承单例, 测试一下吧:
@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
复制代码
继续运行原来的 test case, 崩了:
sharedInstance
方法发生了递归调用, 致使
dispatch_once
死锁了:
弊端:没法实现单例继承
仔细观察上个版本的崩溃堆栈, 发现问题所在就是 allocWithZone:
的实现! 把两个类的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];
}
复制代码
执行测试用例:
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>
复制代码
🎉🎉🎉 大功告成~~~
放到项目中跑起来, 貌似隐约感受不对~~~ 有些单例中的状态怎么被reset 了? 添加一些生命周期方法, 加上日志测试。。。 原来问题在-init
上!
分别给父类和子类添加以下 -init
方法:
- (instancetype)init {
self = [super init];
NSLog(@"%@ call %s", self, __PRETTY_FUNCTION__);
return self;
}
复制代码
继续运行测试用例, 输出以下:
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
方法都会被执行一次, 单例中的状态也就会丢失了~。
弊端:没法保证初始化方法不可重入。
咱们在项目中, 为了减小重复代码, 把单例的实现写成一个模板, 只须要把这个宏添加到类实现中, 就能把这个类变成单例。详情能够参考我好久好久之前的文章。
如何保证初始化方法不可重入呢? 这个问题我想了很久, 貌似除了在-init
方法中添加初始化标记, 没有其余办法了。 可是如何在 -init
中添加标记呢? 我能想到的办法有俩:
-init
方法。 咱们能够给每一个单例增长一个 category, 而后在 category 中实现+load
方法(不用担忧会覆盖主类中的+load
, 每一个 category 均可以添加本身的+load
方法, 并且这些+load
方法都会被执行), 在这里替换掉-init
。-init
, 就能够增长这个标记了, 而后定义一个新的初始化方法 -singletonInit
, 在 -init
中调用就能够了。外部单例类只须要实现这个 -singletonInit
就能够了。通过仔细考虑, 我最后选择了方案二, 主要是 method swizzle 风险不太可控, 方案二虽然保守, 可是比较可靠。
修改一下单例 -init
方法实现:
// 父类, 子类也相似
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);
}
复制代码
继续运行测试用例, 结果以下:
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>
复制代码
此次好像没问题了, 不会重复执行-init
方法了。 但是子类的初始化貌似不太对?由于咱们如今修改了-init
方法, 真正的类的初始化是在-init
里的-singletonInit
里边进行的, 所以子类的初始化也必须调用父类的方法, 这样才能保证彻底初始化。 因此咱们必须在-singletonInit
中调用 super 方法。 但是问题来了, -singletonInit
是须要开发者本身实现的, 怎么保证开发者不会忘记添加[super singletonInit]
呢? 你们可能会想起, 在 xcode 中写 viewController 的时候,-viewWillAppear:
等方法, 若是不写 supper 调用, 就会出编译警告, 提示你必须调用 super 方法。 这就是利用了 llvm 的编译属性, 苹果已经把它封装成一个宏:NS_REQUIRES_SUPER
。 因此咱们继续添加以下代码:
/// .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)
复制代码
对象被释放以后, 不再能继续建立单例了, 获得的都是nil
。 缘由就是, dispatch_once
, 得换个方法。
弊端:不支持 weak 单例
咱们把+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单例实现, 咱们用宏把它变成一个模板:
#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> */
///////////////////////////////////////////////////////////////////////////////////////////////
/// 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
复制代码
@interface NSObject (ALSingletonInit)
- (void)singletonInit NS_REQUIRES_SUPER;
@end
复制代码
#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 实现一个完整的单例, 须要注意如下几点:
alloc
init
必须保证“原子性”,不然在多线程状况下就会出现 ThreadA 执行完 alloc, 而后另一个线程就有可能获取到的是这个刚 alloc 出来还没执行 init 的实例,致使意外状况。int
必须保证只能执行一次。