什么是单例模式
- 单例模式就是要保证系统中一个类只有一个对象实例。无论用什么方法创建多少次,所得的对象都是同一个对象。
单例模式的应用场景
- 在iOS开发中,我们已经遇到过很多单例模式的身影:
- [UIApplication sharedApplication]、[NSUserDefaults standardUserDefaults]、[NSNotificationCenter defaultCenter] 等等。
- 音乐播放器中用于播放音乐的播放器对象、一个APP中用于保存并且能够随时方便地获取的用户信息的对象 等等。
单例模式的关键
对象只创建一次
可供全局访问
不会被释放,直至程序结束
单例模式的分析与实现
- 对象只创建一次:
在iOS中我们创建一个对象一般用:alloc init 或 new,其中new方法内部实际也是通过alloc init创建的,所以我们把注意力放在alloc init上。首先alloc方法是给对象分配内存空间,然后init方法是对该对象进行初始化,所以想要控制对象的创建关键是在alloc方法上,又由于alloc默认是调用allocWithZone方法,所以我们应该重写allocWithZone方法来控制对象只能创建一次:
id instance; // 定义全局变量来保存单例对象,此处还不完善,后面还会提到
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
// 此处if判断可以避免频繁加锁,只要对象已创建就直接返回,亦相当于懒加载
if (instance == nil) {
// 方法一:互斥锁方式
@synchronized(self) {
if (instance == nil) {
instance = [super allocWithZone:zone]; // 用super去调用,避免死循环
}
}
// 方法二:GCD一次性代码方式
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone]; // 用super去调用,避免死循环
});
}
return instance;
}
-
可供全局访问:
单例模式要提供一个类方法来获取单例对象,例如:Tools *tools = [Tools sharedTools]; UserTool *userTool = [UserTool defaultUserTool];
实现如下:
// 单例类方法 命名规则: shared + 类名 或 default + 类名 + (instancetype)sharedTools { if (instance == nil) { instance = [self alloc] init]; // 最终还是调用allocWithZone方法 } return instance; }
-
不会被释放,直至程序结束:
在第一个关键点中,我们定义了一个全局变量id instance;
来保存我们创建的单例对象,但是有个弊端,如果在别的文件中(别的类)使用extern关键字来获取这个对象是可以拿到的,并且可以把该对象销毁,例如:extern id instance; instance = nil;
这样以来,下次再获取单例对象的时候发现为nil就会重新创建对象,即二次创建对象,亦即不为单例模式,为了防止单例对象的销毁,我们应该使用static修饰用于保存单例对象的变量,限制变量的作用域为此文件可用,那么别的文件(别的类)就无法拿到这个对象,从而达到单例对象不会被释放。
即把id instance;
改为static id instance;
严谨的单例模式
-
创建对象除了alloc init 和 new 以外,还可以通过copy 和 mutableCopy来创建对象,为了严谨起见,我们还需要控制这两个方法的创建过程,即需要重写copyWithZone和mutableCopyWithZone方法,重写这两个方法需要分别遵守NSCopying 和 NSMutableCopying协议。
因为这两个方法是对象方法,所以当想要使用这两个方法来创建新对象的时候,只能是用单例对象来调用此方法,即单例对象已经创建了,所以我们只需要返回我们保存的单例对象即可,代码如下:- (id)copyWithZone:(NSZone *)zone { return instance; } - (id)mutableCopyWithZone:(NSZone *)zone { return instance; }
至此一个严谨的单例设计模式已经完成了,下面附上完整代码:
#import "Tools.h"
@implementation Tools
static id instance;
+ (instancetype)sharedTools {
if (instance == nil) {
instance = [[self alloc] init];
}
return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
// 此处if判断可以避免频繁加锁,只要对象已创建就直接返回,亦相当于懒加载
if (instance == nil) {
// 方法一:互斥锁方式
@synchronized(self) {
if (instance == nil) {
instance = [super allocWithZone:zone]; // 用super去调用,避免死循环
}
}
// 方法二:GCD一次性代码方式
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone]; // 用super去调用,避免死循环
});
}
return instance;
}
// 遵守NSCopying协议
- (id)copyWithZone:(NSZone *)zone {
return instance;
}
// 遵守NSMutableCopying协议
- (id)mutableCopyWithZone:(NSZone *)zone {
return instance;
}
@end