首页 > 代码库 > 单例模式(Singletond)
单例模式(Singletond)
定义
单例模式是为了使得整个程序的单例类只有一个对象,整个程序共同使用一个该类型的对象。单例模式确保某一个类只有一个实例,这个类称为单例类。其定义如下:
单例模式其实很容易理解的,我只要一个对象,至始至终都是这一个对象。对于C++实现单例模式是比较简单的,把构造函数(包括拷贝构造函数)、析构函数的访问权限设置为private,然后提供获取单例对象的接口即可(一般不提供销毁对象的接口,防止不小心销毁了又创建)。Objective-C实现单例模式的思想大致也是相同的,但是Objective-C实现起来却比C++难的多。这主要是Objective-C的类不是单纯的类,每个类几乎都需要继承自NSObject,而NSObject很多人不是很了解,因此对于Objective-C由什么方式去创建对象、销毁对象、复制对象不是太清楚,因此未能保证单个创建对象,防止销毁对象的情况发生。Objective-C不像C++提供了构造函数,因此不能通过屏蔽构造函数来防止对象的创建,相反,Objective-C的创建对象的接口对外都是可用的,所以我们需要的不是屏蔽创建对象的方法,而是修改定制这些创建对象的方法,使得这些创建对象的方法返回的都是同一个对象。
单例模式的实现
如果你没有看过Objective-C中实现单例模式的例子,你大概会这样实现单例模式:
#import "WrongSingleton.h" static WrongSingleton* sharedInstances = nil; @implementation WrongSingleton + (id) shareInstances { if(sharedInstances == nil) { sharedInstances = [[WrongSingleton alloc] init]; } return sharedInstances; } - (id) init { if (self == nil) { self = [super init]; // 其他的初始化 } return self; } @end其中头文件声明了接口+ (id) shareInstances,但是这种实现却实实在在是有问题的,最直接的证据是下面的代码创建了不同的对象。
/** * 单例模式不支持copy和mutableCopy方法 */ /** * 错误的单例模式测试用例 */ WrongSingleton* w1 = [WrongSingleton shareInstances]; WrongSingleton* w2 = [[WrongSingleton alloc] init]; NSLog(@"1-%@ 2-%@", w1, w2); /** * 输出结果是 * 1-<WrongSingleton: 0x100111130> 2-<WrongSingleton: 0x1001111c0> * 两个不同的对象,所以WrongSingleton的不符合单例模式的 */这时因为两行代码都使用了系统定义的alloc方法,所以能够创建两个不同的对象,那么现在如何去改正呢?
Objective-C关于对象修改的主要接口方法列表如下所示:
/** * @author Arbboter, 15-01-15 22:01:57 * 创建一个当前类的实例变量所需的内存 * @param zone 已经忽略 * @return 分配的对象 */ + (instancetype)allocWithZone:(struct _NSZone *)zone; /** * @author Arbboter, 15-01-15 23:01:06 * 由于历史问题,该方法会调用allocWithZone方法创建对象内存 * @return 分配了内存的对象 */ + (instancetype)alloc; /** * 初始化对象的isa为描述类的数据结构,其他的实例变量为0 */ - (instancetype)init; /** * @author Arbboter, 15-01-15 23:01:57 * 相当于 [[类名 alloc] init] * @return 初始化好的对象 */ + (instancetype)new; /** * @author Arbboter, 15-01-15 23:01:49 * 由NSCopying协议定义的copyWithZone:方法返回的对象 * @return copy出的对象 */ - (id)copy; /** * @author Arbboter, 15-01-15 23:01:49 * 由NSMutableCopying协议定义的mutableCopyWithZone:方法返回的对象 * @return copy出的对象 */ - (id)mutableCopy; /** copy协议 */ + (id)copyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE; + (id)mutableCopyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE; /** 释放销毁对象 */ - (void)dealloc;当然,上述的接口主要是NSObject的方法,还有其他的比如retain之类的没有列举。上述的接口中,需要注意的是+ (instancetype)allocWithZone:(struct _NSZone *)zone;接口,这是因为这是Objective-C编程所能看到的分配对象内存的源头,alloc也是会调用该接口来分配内存创建对象的。因此我们一定是要对这个接口动手术的,保证这个接口返回的对象都是同一个对象。此外,有一点需要注意到,代码[super init]返回的都是同一个对象的。了解了Objective-C对象的修改对象接口,我们差不多就可以开始实现自己的单例类了。为什么是差不多了,这是因为我们还得知道我们的单例类是面向ARC代码还是非ARC代码。
ARC的单例模式设计
ARC的单例模式相对简单些。其单例类的实现代码示例如下所示:
#import "ARCSingleton.h" static ARCSingleton* sharedInstances = nil; @implementation ARCSingleton + (id) shareInstances { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstances = [[super allocWithZone:nil] init]; // 其他代码 }); return sharedInstances; } // allocWithZone是创建对象分配内存的源头,alloc也会调用该方法 + (id)allocWithZone:(struct _NSZone *)zone { return [[self class] shareInstances]; } @end上述代码的+ (id) shareInstances接口中使用GCD来确保线程安全问题,保证只会调用一次创建对象的代码。其测试用例如下所示:
/** * ARC版本的单例模式测试 */ ARCSingleton* a2 = [[ARCSingleton alloc] init]; ARCSingleton* a1 = [ARCSingleton shareInstances]; NSLog(@"1-%@ 2-%@", a1, a2); /** * 输出结果是 * 1-<ARCSingleton: 0x100111c30> 2-<ARCSingleton: 0x100111c30> * 地址相同,是相同的对象 */重点在于自定义的+ (instancetype)allocWithZone:(struct _NSZone *)zone;接口,防止外部直接或者间接调用该接口分配内存创建对象,而且这里的+ (id) shareInstances接口使用GCD+父类的allocWithZone:接口分配内存创建对象。
非ARC模式的单例模式设计
非ARC模式的,APPLE有个例子,其实现相对而言复杂一点,需要考虑的因素比较多,下面的代码主要根据APPLE官方提供的代码修改出来的,知识增加了多线程安全部分的控制,其大致形式如下所示:
#import "Singleton.h" static Singleton* sharedInstances = nil; // 非ARC版本的单例模式 @implementation Singleton + (id) shareInstances { // 线程同步,Apple官方的例子没有加synchronized @synchronized(self) { if(sharedInstances == nil) { sharedInstances = [[super allocWithZone:nil] init]; } } return sharedInstances; } // allocWithZone是创建对象分配内存的源头,alloc也会调用该方法 + (id)allocWithZone:(NSZone *)zone { return [[self shareInstances] retain]; } - (id)copyWithZone:(NSZone *)zone { return self; } - (id)retain { return self; } - (NSUInteger)retainCount { //denotes an object that cannot be released return NSUIntegerMax; } - (oneway void)release { // never release } - (id)autorelease { return self; } - (id)init { /** * 在同一个类里面调用[super init]返回的对象都是相同的 */ if (self = [super init]) { } return self; } - (void)dealloc { [super dealloc]; } @end测试用例为:
/** * 非ARC版本的单例模式测试 */ Singleton* s2 = [[Singleton alloc] init]; Singleton* s1 = [Singleton shareInstances]; NSLog(@"1-%@ 2-%@", s1, s2); /** * 输出结果是 * 1-<Singleton: 0x100106f60> 2-<Singleton: 0x100106f60> * 各个对象的地址都是相同的 */
总结
单例模式是为了整个程序共享使用同一个对象,因此该单例类不提供copy之类的方法。
参考
- APPLE官方的单例模式
- Objective-C对象模型分析
- 破船之家的单例模式实现
单例模式(Singletond)