线程安全的单例
在软件工程中,单例是一种用于实现单例的数学概念,即将类的实例化限制成仅一个对象的设计模式。
单例是一种特殊类,该类只能实例化一个对象。
本文写出了两种线程安全的单例,一种利用GCD中得dispatch_once,另一种利用@synchronized(self){} 互斥锁;
此外封堵alloc,copy方法,致使不能通过这两种方法创建实例。重写-allocWithZone与-copyWithZone来分别实现alloc,copy方法的封堵;
1 #import "YTSingleton.h" 2 static YTSingleton *s = nil; 3 @implementation YTSingleton 4 //线程不安全的单例方法实现 5 + (instancetype)sharedSingletonNotSafe 6 { 7 if (s == nil) { 8 s = [[self alloc]init]; 9 } 10 return s; 11 } 12 //线程安全的单例方法 13 + (instancetype)sharedSingleton 14 { 15 static dispatch_once_t onceToken; 16 dispatch_once(&onceToken, ^{ 17 s = [[self alloc]init]; 18 }); 19 return s; 20 } 21 22 //线程安全的单例方法 23 + (instancetype)sharedSingleton2 24 { 25 @synchronized(self){ 26 if (s == nil) { 27 s = [[self alloc]init]; 28 } 29 } 30 return s; 31 } 32 33 //当alloc当前类的对象时自动调用的方法 34 + (instancetype)allocWithZone:(struct _NSZone *)zone 35 { 36 if (s == nil) { 37 s = [super allocWithZone:zone]; 38 return s; 39 } 40 return nil; 41 } 42 43 - (id)copyWithZone:(NSZone *)zone 44 { 45 return self; 46 } 47 @end
1. dispatch_once
void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);
如果被多个线程调用,该函数会同步等等直至代码块完成。
1、synchronized关键字的作用域有二种:
1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要 一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是 不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作 用。
2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){},它的作用域是当前对象;(显然本文中用到的时这种用法)
3. allocWithZone
首先我们应该知道 [[Class alloc] init],其实是做了两件事。 alloc 给对象分配内存空间,init是对对象的初始化,包括设置成员变量初值这些工作。而给对象分配空间,除了alloc方法之外,还有另一个方法: allocWithZone.在NSObject 这个类的官方文档里面,allocWithZone方法介绍说,该方法的参数是被忽略的,正确的做法是传nil或者NULL参数给它。而这个方法之所以存在,是历史遗留原因。Do not override allocWithZone: to include any initialization code. Instead, class-specific versions of init… methods.This method exists for historical reasons; memory zones are no longer used by Objective-C.
文档里面提到,memory zone已经被弃用了,只是历史原因才保留这个接口。详细是什么历史原因我没找到,不过后面介绍的内容会稍微涉及到。而实践证明,使用alloc方法初始化一个类的实例的时候,默认是调用了 allocWithZone 的方法。于是覆盖allocWithZone方法的原因已经很明显了:为了保持单例类实例的唯一性,需要覆盖所有会生成新的实例的方法,如果有人初始化这个单例类的时候不走[[Class alloc] init] ,而是直接 allocWithZone, 那么这个单例就不再是单例了,所以必须把这个方法也堵上。allocWithZone的答案到此算是解决了,但是,问题是无止境的。
4. copyWithZone
-copy,此方法存在的意义只是为了方便,里面会直接把-copyWithZone的方法返回。但是NSObject并没有实现-copyWithZone,需要子对象去实现NSCopying协议(即实现-copyWithZone方法)。
最后NSZone这玩艺儿已经被Apple抛弃,所以NSObject的copy方法中只是传入NULL。