单例
和其它语言的单例产不多,可以说是最简单的一种设计模式了。但是有几个点需要注意下,单例就是一个类只有一个实例。
所以我们要想办法阻止该类产生别的实例,一般语言中都会将构造函数写为private。但是OC中的函数并没有限定符,所以我们需要用一些小技巧来屏蔽这一点。
应用场景
类只能有一个实例,而且必须从一个为人熟知的访问点对其进行访问,比如工厂方法。
这个唯一的实例只能通过类的子类化进行扩展,而且扩展的对象不会破坏客户端代码。
注意
1.OC中单例的实例变量要定义在.m文件
2.OC中单例需要重载allocWithZone:和copyWithZone:方法来防止创建别的实例。
3.单例创建要注意线程安全,不然就可能出现多个实例。
注意问题将会在Demo中讲解
Demo
首先先来看一个最常规,的不严谨的单例实现:
@implementation Singleton static Singleton *sharedInstance; -(Singleton *)sharedInstance { if(sharedInstance) { sharedInstance = [Singleton new]; } return sharedInstance; } @end
这看似好像是可以得到单例对象了,但是这可以说是单例的一种变形。绝不能说这就是单例,因为我们可以轻松地通过其他方式来创建对象。
所以我们还要我修改allocWithZone:和copyWithZone:方法(alloc 和 copy 方法实际上就是调用这两个方法)
-(id)copyWithZone:(NSZone *)zone { return [[self class] sharedInstance]; } +(id)allocWithZone:(struct _NSZone *)zone { return [self sharedInstance]; }
可是这就出现另一个问题,在sharedInstance方法里面我们实际调用过allocWithZone:(new 调用 alloc),但是它的alloc被我们重写了,这就会出现错误。所以我们需要修改sharedInstance方法
+(Singleton *)sharedInstance { if(sharedInstance) { sharedInstance = [[super allocWithZone:NULL] init]; } return sharedInstance; }
这样就可以顺利的返回单例了,而且无法通过其它方式产生实例对象。
看似完美了实际还会有问题出现,因为现在是非线程安全的,可能存在同一时间创建多个实例的情况,所以修改如下
+(instancetype)sharedInstance { static dispatch_once_t once; dispatch_once(&once, ^{ sharedInstance = [[super allocWithZone:NULL] init]; }); return sharedInstance; }
客户端代码如下:
Singleton *singleton = [Singleton sharedInstance]; Singleton *singleton2 = [[Singleton alloc] init]; Singleton *singleton3 = [singleton copy]; [singleton print]; [singleton2 print]; [singleton3 print];
输出结果:
2015-07-21 21:10:32.797 Singleton[42537:10347987] 0x7fff5fbff7a8 2015-07-21 21:10:32.798 Singleton[42537:10347987] 0x7fff5fbff7a8 2015-07-21 21:10:32.798 Singleton[42537:10347987] 0x7fff5fbff7a8
可以看到内存都指向了同一地址。