转载

IOS开发模式-单例模式

开篇先扯淡

去重庆玩了一趟之后,还是要老老实实坐下来写代码的。哈哈哈。不过重庆人民的串串和老火锅真的一绝。。。扯完题外话。开始今天的正题,打算一个钟把文章写完,不知道可以不可以。

正文

其实小编在接触了苹果的新的开发语言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; / }
正文到此结束
Loading...