单例模式是什么,这里姑且不谈,只谈谈ARC+GCD实现下单例模式比较特殊和受争议的点。
首先看一个ARC+GCD实现:
@interface Singleton : NSObject
+ (Singleton *)sharedInstance;
@end
@implementation Singleton
static Singleton *sharedInstance = nil ;
+ (Singleton *) sharedInstance
{
static dispatch_once_t onceToken; // 锁
dispatch_once (& onceToken, ^ { // 最多调用一次
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
@end
这个实现很简单,但是并不能防止用户通过[[Singleton alloc]init]创建多个实例,通过小小的测试即可:
Singleton *signleton1= [Singleton sharedInstance];
NSLog(@"singleton1 is %@",signleton1);
Singleton *signleton2= [[Singleton alloc]init];
NSLog(@"singleton2 is %@",signleton2);
可以看出多个实例被创建出来了。
怎么防止这一点呢?我们需要注意的几点:
1.Objective-c并不像C++那样,可以直接将构造函数设成private,使外部不能调用
2.只能考虑在alloc分配内存的时候把路给堵死
3.alloc中实际上调用了allocWithZone:,就算把alloc堵死了,也不能防止allocWithZone被用户直接调用
–>>所以我们必须通过allocWithZone将用户堵死
参考非ARC的实现,我们很容易写出如下所示的代码:
+(instanceType)allocWithZone:(struc _NZone)zone
{
return [self sharedInstance];
}
但一运行,会发现程序hang住了。分析程序可知,程序在block中block住了。同样考虑到线程安全,allocWithZone的代码应该这样写:
+(instanceType)allocWithZone:(struc _NZone)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
sharedInstance = [super allocWithZone:zone];
});
return sharedInstance;
}
再运行之前的测试代码,这回单例只有一个了。