所有转出博客园,请您注明出处:http://www.cnblogs.com/xiaobajiu/p/4122034.html
Objectic-C的单例的两种安全实现方案
首先应该知道单例的实现有两大类,一个是懒汉式,一个是饿汉式。所谓的懒汉式就是在我们用到某对象(资源)的时候,去问一个负责提供的方法要那个对象。那个方法发现没有这个资源时就去创建资源,如果是已经有该资源时就直接返回这个资源。而饿汉式就是那个负责提供的方法早已为我们准备好了我们想要的资源问它,它就提供给我们那个它早就准备好了的资源。
饿汉式的实现是简单而且好理解,但是它的理念不适合移动设备,因为饿汉式提前占用了内存,却不管我们需不需要。所以饿汉式的实现在最后作为一点补充。我们主要使用懒汉式,它符合移动的开发理念。
首先理清实现单例模式(懒汉式)的思路:
1.依靠什么来单例?
答:依靠静态变量,因为它在内存中只有一份。只要判断这个变量存不存在我们就知道需不需要创建一个对象赋值给那个静态变量。
2.但是在并发的情况下,我们同时去判断一个变量都得到了nil的结果,这两者就会都去创建一个新对象,那么会导致唯一性失效,就会使这两次访问得到不同的结果了,怎么解决?
答:加锁或者使用dispatch_once。
请先看加锁的方案。多说两句,加锁@synchronized()的效率可不是java,c#那么高的。《cocao开发者编程手册》47页这样写道:“这需要精确的垃圾回收,以及重写本地二进制文件的能力,Objectic-C编译器这两件事都做不了。所以,这个关键字非常低效”。但其实我们是可以避免频繁加锁,在网上找到的大多数代码都没有考虑到频繁加锁的问题的,而这里解决了这个问题,直接看代码吧:
// // God.h #import <Foundation/Foundation.h> @interface God : NSObject<NSCopying> + (God*)sharedGod; @end
// // God.m #import "God.h" static God *_singleGod; @implementation God + (id)allocWithZone:(struct _NSZone *)zone { if(!_singleGod){//防止频繁加锁 @synchronized([God class]){ if(!_singleGod) _singleGod= [super allocWithZone:zone]; } } return _singleGod; } + (God*)sharedGod { if(!_singleGod){//防止频繁加锁 @synchronized([God class]){ if(!_singleGod){ _singleGod= [[self alloc] init]; } } } return _singleGod; } - (id)copyWithZone:(NSZone *)zone { return _singleGod; } //MRC下增加下面四个方法即可 - (id)retain { return self; } - (NSUInteger)retainCount { return 1; } - (oneway void)release {} - (id)autorelease { return self; } @end
加锁的方案看起来似乎过于臃肿,使用GCD提供的dispatch_once来解决,将会使代码看起来简洁有力。(注:dispatch_once是GCD提供的一次性代码方案,该方法在整个程序的生命周期中只会执行一次。下一次执行到这里不会再执行了。)
dispatch_once版的实现如下:
// // God2.m #import "God2.h" static God2 *singleGod2; @implementation God2 + (id)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ singleGod2= [super allocWithZone:zone]; }); return singleGod2; } + (instancetype)sharedGod2 { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ singleGod2= [[self alloc] init]; }); return singleGod2; } - (id)copyWithZone:(NSZone *)zone { return singleGod2; } //MRC下增加下面四个方法即可 - (id)retain { return self; } - (NSUInteger)retainCount { return 1; } - (oneway void)release { } - (id)autorelease { return self; } @end
使用dispatch_once来实现单例,我们更本不需要担心线程安全问题,因为GCD提供的解决方案本身就是线程安全的。大大简化了开发难度。
最后简单的补充一点关于懒汉式的写法,那么要知道+(void)load这个方法。它是类型加载到runtime时调用该方法,并且仅此一次。它的核心就是下面一行代码:
+ (void)load { singleGod3= [[self alloc] init]; }
欢迎各位朋友指正,以免误人子弟。