开篇先扯淡
去重庆玩了一趟之后,还是要老老实实坐下来写代码的。哈哈哈。不过重庆人民的串串和老火锅真的一绝。。。扯完题外话。开始今天的正题,打算一个钟把文章写完,不知道可以不可以。
正文
其实小编在接触了苹果的新的开发语言swift之后,深刻觉得单例模式在swift里面体现的淋漓尽致,不过在OC里面确实会有一些麻烦。简单对比一下OC的单例模式和swift的单例模式吧。拿小编之前的一个程序开刀。
struct DeviceData { static let height = UIScreen.mainScreen().bounds.height static let width = UIScreen.mainScreen().bounds.width static let bound = UIScreen.mainScreen().bounds }
再来看一下oc版本的单例
@implementation HMMusicTool static id _instance; /** * alloc方法内部会调用这个方法 */ + (id)allocWithZone:(struct _NSZone *)zone { if (_instance == nil) { // 防止频繁加锁 @synchronized(self) { if (_instance == nil) { // 防止创建多次 _instance = [super allocWithZone:zone]; } } } return _instance; } + (instancetype)sharedMusicTool { if (_instance == nil) { // 防止频繁加锁 @synchronized(self) { if (_instance == nil) { // 防止创建多次 _instance = [[self alloc] init]; } } } return _instance; } - (id)copyWithZone:(NSZone *)zone { return _instance; } @end
这时候swift的优势可见一斑吧。不过小编还是会辩证的去看待问题的,swift还是存在一些兼容性的问题的。这次小编先拿oc开刀。底层实现都是一样的。
简单介绍单例模式 防止大家百度,小编直接搬来了烂大街的介绍
单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
1.单例模式的要点:
显然单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
2.单例模式的优点:
1.实例控制:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例。2.灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程
那么如何优雅的实现oc的单例模式呢。
ARC + 互斥锁版本
第一步。 为单例对象实现一个静态实例,并初始化,然后设置成nil
第二步。检查静态实例的值是否为nil
第三步。重写 allocWithZone, copyWithZone 。
第四步。加锁
代码如下:
+ (id)allocWithZone:(struct _NSZone *)zone { if (_instance == nil) { // 防止频繁加锁 @synchronized(self) { if (_instance == nil) { // 防止创建多次 _instance = [super allocWithZone:zone]; } } } return _instance; } + (instancetype)sharedMusicTool { if (_instance == nil) { // 防止频繁加锁 @synchronized(self) { if (_instance == nil) { // 防止创建多次 _instance = [[self alloc] init]; } } } return _instance; } - (id)copyWithZone:(NSZone *)zone { return _instance; } @end
调用
HMMusicTool *tool = [[HMMusicTool alloc] init]; HMMusicTool *tool2 = [[HMMusicTool alloc] init]; HMMusicTool *tool3 = [HMMusicTool sharedMusicTool]; HMMusicTool *tool4 = [HMMusicTool sharedMusicTool];
tool。tool2.tool3.tool4的地址是一致的。简单分析一下上面的程序。类HMMusicTool在alloc的时候会调用 allocWithZone方法。所以重写 allocWithZone方法。
@synchronized(self) { if (_instance == nil) { // 防止创建多次 _instance = [super allocWithZone:zone]; } }
判断是否创建多次。然后加一个互斥锁,防止在多线程中创建多个对象,返回对象_instance.
除了通过alloc创建之外,还可以通过类方法创建,典型的创建方法例如 [ UIApplication sharedApplication ]然后我们创建一个类方法。
+ (instancetype)sharedMusicTool { if (_instance == nil) { // 防止频繁加锁 @synchronized(self) { if (_instance == nil) { // 防止创建多次 _instance = [[self alloc] init];//如果第一次使用。调用allocWithZone。 } } } return _instance; }
当然除了以上调用方式外。还有可能通过copy实现对象的复制。还需要重写copy方法。这时候需要遵循NSCoping协议。
- (id)copyWithZone:(NSZone *)zone { return _instance; }
当调用copyWithZone的时候,往往已经实例化了一个变量。这时候_instance是存在的。所以直接返回即可。
为什么_instance要加上static呢。
_instance作为全局变量。如果不加上static。则在程序的其他文件里面可以通过extern访问。对_instance的值进行更改,这一点是非常危险的。但是加上static。_instance只能在当前文件访问。其他文件访问出错。
ARC + GCD版本
相对互斥锁版本没有相差太多,只是使用了GCD的一个方法。
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ <#code to be executed once#> });
闭包里面的代码只能被执行一次。所以更改后的程序为
// 用来保存唯一的单例对象 static id _instace; + (id)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instace = [super allocWithZone:zone]; }); return _instace; } + (instancetype)sharedDataTool { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instace = [[self alloc] init]; }); return _instace; } - (id)copyWithZone:(NSZone *)zone { return _instace; } @end
非ARC版本 单例模式
非ARC版本需要手动管理内存,所以单例模式需要防止单例被释放,这时候需要重写几个方法
- (oneway void)release { } - (id)retain { return self; } - (NSUInteger)retainCount { return 1;} - (id)autorelease { return self;}
这样子就防止了单例被释放,保持单例的计数为1。全部代码为GCD+非ARC
static id _instace; + (id)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instace = [super allocWithZone:zone]; }); return _instace; } + (instancetype)sharedDataTool { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instace = [[self alloc] init]; }); return _instace; } - (id)copyWithZone:(NSZone *)zone { return _instace; } - (oneway void)release { } - (id)retain { return self; } - (NSUInteger)retainCount { return 1;} - (id)autorelease { return self;}
总结
单例在程序开发当中,有着举足轻重的地位。当有多个单例对象需要创建的时候,可以考虑把单例抽出来作为宏,不过相对来说调试来说确实会比较坑。不过一切为了简单嘛。如下
// .h文件 #define HMSingletonH(name) + (instancetype)shared##name; // .m文件 #define HMSingletonM(name) / static id _instance; / / + (id)allocWithZone:(struct _NSZone *)zone / { / static dispatch_once_t onceToken; / dispatch_once(&onceToken, ^{ / _instance = [super allocWithZone:zone]; / }); / return _instance; / } / / + (instancetype)shared##name / { / static dispatch_once_t onceToken; / dispatch_once(&onceToken, ^{ / _instance = [[self alloc] init]; / }); / return _instance; / } / / - (id)copyWithZone:(NSZone *)zone / { / return _instance; / }