本文的完整代码在这里的DesignPatternDemo
目录
初始版本
同步锁版本
GCD版本
alloc版本
copy版本
小结
引言
开发中最常见也是最简单的设计模式莫过于单例模式了
但是写好一个单例模式其实并没有那么简单
鉴于它的难易适中, 所以经常会出现在各种笔试面试题中
废话不多说, 我们来实际演练瞧瞧
初始版本
说写咱就写, 不出两分钟单例就写好了
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
if (!sharedInstance) {
sharedInstance = [self new];
}
return sharedInstance;
}
接着我们来测试一下
SingletonClass *singletonObject = [SingletonClass sharedInstance];
NSLog(@"%p", singletonObject);
singletonObject = [SingletonClass sharedInstance];
NSLog(@"%p", singletonObject);
打印结果如下
0x7fab72708d90
0x7fab72708d90
同步锁版本
上面的写法, 没有考虑多线程并发问题, 所以我们加个同步锁
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
@synchronized(self) {
if (!sharedInstance) {
sharedInstance = [self new];
}
}
return sharedInstance;
}
GCD版本
GCD提供了dispatch_once接口来保证代码只会被执行一次, 相比同步锁效率更高
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [self new];
});
return sharedInstance;
}
alloc版本
这下程序应该完美了, 可以收工了
别急, 万一别人是用这种方式获取SingletonClass对象的呢?
别为我为什么要用这个方式, 我还要问你别人为什么只使用sharedInstance方式呢!
SingletonClass *singletonObject = [SingletonClass sharedInstance];
NSLog(@"%p", singletonObject);
singletonObject = [[SingletonClass alloc] init];
NSLog(@"%p", singletonObject);
打印结果如下
0x7fb4d3f25140
0x7fb4d3e0c950
为了解决这个问题, 我们需要修改sharedInstance方法并覆盖allocWithZone方法
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] init];
});
return sharedInstance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [self sharedInstance];
}
重新运行测试用例, 打印结果如下
0x12dd2bcb0
0x12dd2bcb0
copy版本
这下程序应该完美了吧, 总算可以收工了吧
说了不要急, 我们还没有测试copy对象的情形呢
SingletonClass *copyedObject = [singletonObject copy];
NSLog(@"%p", copyedObject);
重新运行程序看下打印结果吧, 怎么样? crash了!
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SingletonClass copyWithZone:]: unrecognized selector sent to instance 0x14766dab0'
找不到copyWithZone方法, 说明我们也需要覆盖该方法
+ (id)copyWithZone:(struct _NSZone *)zone {
return [self sharedInstance];
}
- (id)copy {
return self;
}
这下再运行上面所有的测试用例
打印结果如下
0x136641720
0x136641720
0x136641720
小结
上述单例的写法已经可以满足大部分的需求了, 所以我们就不继续深究和完善了
小结个吧, 单例的完整定义如下
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] init];
});
return sharedInstance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [self sharedInstance];
}
+ (id)copyWithZone:(struct _NSZone *)zone {
return [self sharedInstance];
}
- (id)copy {
return self;
}
PS: 最后应该给单例模式一个权威定义的, 所以摘录Big Four的设计模式中的定义如下
Ensure a class has only one instance, and provide a global point of access to it.
确保某一个类只有一个实例, 而且自行实例化并向整个系统提供这个实例
更多文章, 请支持我的个人博客