IOS开发模式-单例模式

jopen 9年前

正文

其实小编在接触了苹果的新的开发语言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; \  }

来自: http://www.goofyy.com/blog/ios开发模式-单例模式/