使用dispatch_once实现单例模式

单例模式是一种经常使用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。经过单例模式能够保证系统中一个类只有一个实例并且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。若是但愿在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。html

在iOS中,UIApplication,NSNotificationCenter,NSFileManager等类都是单例模式的实例。设计模式

在ARC环境下,结合GCD能够实现单例模式:安全

单例类Singleton类的接口部分:app

  1. #import <Foundation/Foundation.h>  函数

  2.   

  3. @interface  Singleton : NSObject <NSCopying>  性能

  4.   

  5. + (Singleton *)sharedInstance;  测试

  6.   

  7. @property (strongnonatomicNSString *str;  优化

  8.   

  9. - (void)print;  atom

  10.   

  11. @end   spa




其中经过sharedInstance方法能够获取该类的单例。

实现部分:

  1. #import "Singleton.h"  

  2.   

  3. @implementation Singleton  

  4.   

  5. __strong static Singleton *singleton = nil;  

  6.   

  7. + (Singleton *)sharedInstance {  

  8.     NSLog(@"调用sharedInstance方法");  

  9.       

  10.     static dispatch_once_t onceToken = 0;  

  11.     dispatch_once(&onceToken, ^{  

  12.         NSLog(@"建立Singleton实例");  

  13.         singleton = [[super allocWithZone:NULL] init];  

  14.         singleton.str = @"Say something";  

  15.     });  

  16.       

  17.     return singleton;  

  18. }  

  19.   

  20. + (id)allocWithZone:(NSZone *)zone {  

  21.     NSLog(@"调用alloc方法");  

  22.     return [self sharedInstance];  

  23. }  

  24.   

  25. - (id)copyWithZone:(NSZone *)zone {  

  26.     NSLog(@"调用copy方法");  

  27.     return self;  

  28. }  

  29.   

  30. - (void)print {  

  31.     NSLog(@"%@"self.str);  

  32. }  

  33.   

  34. @end  




dispatch_once函数确保该类的实例对象只建立一次。

allocWithZone方法和copyWithZone方法确保该类的实例对象不会再次被建立。


关于dispatch_once函数:

使用dispatch_once提价的代码块,即使你提交屡次,只能执行一次。 void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block); 第一个参数是一个传出参数用来保存代码块在队列运行时被赋的值,若是你想让本身的代码只执行一次的话,你必须指定一个一样的标识符,其实它是long类型的长整数,即typedef long dispatch_once_t。 第二个参数是一个代码块,这个代码块没有参数和返回值。 dispatch_once 中的代码块默认的状况下在当前的线程内中执行(也就是被调用函数所在的线程) 

dispatch_once不只意味着代码仅会被运行一次,并且仍是线程安全的。

该方法有不少优点: 
1 线程安全
2 很好知足静态分析器要求
3 和自动引用计数(ARC)兼容 
4 仅须要少许代码


测试结果:

咱们在程序运行的时候建立一个Singleton对象:

  1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  

  2. {  

  3.     Singleton *sin = [Singleton sharedInstance];  

  4.     NSLog(@"---------------------");  

  5.       

  6.     return YES;  

  7. }  


在View Controller类屡次使用sharedInstance方法,alloc init方法和copy方法来尝试建立Singleton类对象:

  1. - (void)viewDidLoad  

  2. {  

  3.     [super viewDidLoad];  

  4.       

  5.     Singleton *singleton = [Singleton sharedInstance];  

  6.     if (singleton) {  

  7.         [singleton print];  

  8.     }  

  9.     NSLog(@"---------------------");  

  10.       

  11.     Singleton *singleton2 = [Singleton sharedInstance];  

  12.     if (singleton2) {  

  13.         [singleton2 print];  

  14.     }  

  15.     NSLog(@"---------------------");  

  16.       

  17.     singleton = nil;  

  18.       

  19.     singleton = [[Singleton alloc] init];  

  20.     if (singleton) {  

  21.         [singleton print];  

  22.     }  

  23.     NSLog(@"---------------------");  

  24.       

  25.     Singleton *singleton3 = [singleton2 copy];  

  26.     if (singleton3) {  

  27.         [singleton3 print];  

  28.     }  

  29. }  


运行结果:

  1. 2014-02-05 21:41:25.323 Singleton[4827:70b] 调用sharedInstance方法  

  2. 2014-02-05 21:41:25.324 Singleton[4827:70b] 建立Singleton实例  

  3. 2014-02-05 21:41:25.324 Singleton[4827:70b] ---------------------  

  4. 2014-02-05 21:41:25.325 Singleton[4827:70b] 调用sharedInstance方法  

  5. 2014-02-05 21:41:25.326 Singleton[4827:70b] Say something  

  6. 2014-02-05 21:41:25.326 Singleton[4827:70b] ---------------------  

  7. 2014-02-05 21:41:25.326 Singleton[4827:70b] 调用sharedInstance方法  

  8. 2014-02-05 21:41:25.327 Singleton[4827:70b] Say something  

  9. 2014-02-05 21:41:25.327 Singleton[4827:70b] ---------------------  

  10. 2014-02-05 21:41:25.327 Singleton[4827:70b] 调用alloc方法  

  11. 2014-02-05 21:41:25.327 Singleton[4827:70b] 调用sharedInstance方法  

  12. 2014-02-05 21:41:25.328 Singleton[4827:70b] Say something  

  13. 2014-02-05 21:41:25.328 Singleton[4827:70b] ---------------------  

  14. 2014-02-05 21:41:25.328 Singleton[4827:70b] 调用copy方法  

  15. 2014-02-05 21:41:25.328 Singleton[4827:70b] Say something  


由控制台输出可见,在程序刚刚启动时经过sharedInstance中的dispatch_once函数建立了一个Singleton实例,随后的sharedInstance方法或alloc init方法或copy方法都不会致使dispatch_once函数的调用,也就是Singleton实例只被建立了一次。


参考资料:

IOS GCD 使用(三)单例模式

dispatch_once优化代码性能

IOS dispatch_once

object-c 单例模式(包括ARC)


后记:

dispatch_once函数的原型为:void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block),其中dispatch_once_t是long类型的typedef,dispatch_block_t是一个代码块的typedef。

这个函数的做用是使得block在整个程序的生命周期中只执行一次,每次调用这段代码时经过predicate来检查,在这里predicate必须严格地初始化为0

能够测试下其输出:

  1. + (UIColor *)boringColor;  

  2. {  

  3.     static UIColor *color;  

  4.     static dispatch_once_t onceToken;  

  5.     NSLog(@"%ld", onceToken);  

  6.     dispatch_once(&onceToken, ^{  

  7.         NSLog(@"dispatch_once");  

  8.         color = [UIColor colorWithRed:0.380f green:0.376f blue:0.376f alpha:1.000f];  

  9.     });  

  10.     NSLog(@"%ld", onceToken);  

  11.     NSLog(@"--------------");  

  12.     return color;  

  13. }  

  14.   

  15. - (void)viewDidLoad  

  16. {  

  17.     [super viewDidLoad];  

  18.       

  19.       

  20.     [Color boringColor];  

  21.     [Color boringColor];  

  22.     [Color boringColor];  

  23. }  


  1. 2014-03-23 14:01:30.653 once_t[1215:60b] 0  

  2. 2014-03-23 14:01:30.655 once_t[1215:60b] dispatch_once  

  3. 2014-03-23 14:01:30.655 once_t[1215:60b] -1  

  4. 2014-03-23 14:01:30.655 once_t[1215:60b] --------------  

  5. 2014-03-23 14:01:30.656 once_t[1215:60b] -1  

  6. 2014-03-23 14:01:30.656 once_t[1215:60b] -1  

  7. 2014-03-23 14:01:30.656 once_t[1215:60b] --------------  

  8. 2014-03-23 14:01:30.656 once_t[1215:60b] -1  

  9. 2014-03-23 14:01:30.657 once_t[1215:60b] -1  

  10. 2014-03-23 14:01:30.657 once_t[1215:60b] --------------  


若onceToken被初始化为0,那么在调用dispatch_once函数时检查到其值为0,就执行block,执行完毕后onceToken减一。下一次调用dispatch_once函数时检查到onceToken = -1,将不会执行block。

下面咱们修改一下:

[objc] view plaincopy在CODE上查看代码片派生到个人代码片

  1. static dispatch_once_t onceToken = 1L;  


控制台输出一直停留在1,程序加载不了、内存保存不变,一直卡在dispatch_once函数那里。

若是将onceToken设置为-2,和设置为1的状况同样。

若是将onceToken设置为-1,不会陷入死循环中,可是block没有被执行,某些变量可能没有被初始化。

可见随意修改onceToken的值有多危险了,同时这也是将其属性设置为static的缘由,由于要在文件域中一直持有其值。


小结:

调用dispatch_once(predicate, block)时,函数首先检查predicate:

若是predicate = 0,那么执行block。

若是predicate = -1,那么不执行block并继续往下执行。

若是predicate != 0 或 -1,那么程序将一直卡在dispatch_once函数那里。

结论:必定要将predicate初始化为0,并将其范围设置为static,另外不要随意改变其值(不要将其设置为类的属性,实例变量等),不然会致使没法预见的行为。

相关文章
相关标签/搜索