單例模式是一種經常使用的軟件設計模式。在它的核心結構中只包含一個被稱爲單例類的特殊類。經過單例模式能夠保證系統中一個類只有一個實例並且該實例易於外界訪問,從而方便對實例個數的控制並節約系統資源。若是但願在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。html
在iOS中,UIApplication,NSNotificationCenter,NSFileManager等類都是單例模式的實例。設計模式
在ARC環境下,結合GCD能夠實現單例模式:安全
單例類Singleton類的接口部分:app
#import <Foundation/Foundation.h> 函數
@interface Singleton : NSObject <NSCopying> 性能
+ (Singleton *)sharedInstance; 測試
@property (strong, nonatomic) NSString *str; 優化
- (void)print; atom
@end spa
實現部分:
#import "Singleton.h"
@implementation Singleton
__strong static Singleton *singleton = nil;
+ (Singleton *)sharedInstance {
NSLog(@"調用sharedInstance方法");
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
NSLog(@"建立Singleton實例");
singleton = [[super allocWithZone:NULL] init];
singleton.str = @"Say something";
});
return singleton;
}
+ (id)allocWithZone:(NSZone *)zone {
NSLog(@"調用alloc方法");
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
NSLog(@"調用copy方法");
return self;
}
- (void)print {
NSLog(@"%@", self.str);
}
@end
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對象:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
Singleton *sin = [Singleton sharedInstance];
NSLog(@"---------------------");
return YES;
}
- (void)viewDidLoad
{
[super viewDidLoad];
Singleton *singleton = [Singleton sharedInstance];
if (singleton) {
[singleton print];
}
NSLog(@"---------------------");
Singleton *singleton2 = [Singleton sharedInstance];
if (singleton2) {
[singleton2 print];
}
NSLog(@"---------------------");
singleton = nil;
singleton = [[Singleton alloc] init];
if (singleton) {
[singleton print];
}
NSLog(@"---------------------");
Singleton *singleton3 = [singleton2 copy];
if (singleton3) {
[singleton3 print];
}
}
2014-02-05 21:41:25.323 Singleton[4827:70b] 調用sharedInstance方法
2014-02-05 21:41:25.324 Singleton[4827:70b] 建立Singleton實例
2014-02-05 21:41:25.324 Singleton[4827:70b] ---------------------
2014-02-05 21:41:25.325 Singleton[4827:70b] 調用sharedInstance方法
2014-02-05 21:41:25.326 Singleton[4827:70b] Say something
2014-02-05 21:41:25.326 Singleton[4827:70b] ---------------------
2014-02-05 21:41:25.326 Singleton[4827:70b] 調用sharedInstance方法
2014-02-05 21:41:25.327 Singleton[4827:70b] Say something
2014-02-05 21:41:25.327 Singleton[4827:70b] ---------------------
2014-02-05 21:41:25.327 Singleton[4827:70b] 調用alloc方法
2014-02-05 21:41:25.327 Singleton[4827:70b] 調用sharedInstance方法
2014-02-05 21:41:25.328 Singleton[4827:70b] Say something
2014-02-05 21:41:25.328 Singleton[4827:70b] ---------------------
2014-02-05 21:41:25.328 Singleton[4827:70b] 調用copy方法
2014-02-05 21:41:25.328 Singleton[4827:70b] Say something
參考資料:
後記:
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。
能夠測試下其輸出:
+ (UIColor *)boringColor;
{
static UIColor *color;
static dispatch_once_t onceToken;
NSLog(@"%ld", onceToken);
dispatch_once(&onceToken, ^{
NSLog(@"dispatch_once");
color = [UIColor colorWithRed:0.380f green:0.376f blue:0.376f alpha:1.000f];
});
NSLog(@"%ld", onceToken);
NSLog(@"--------------");
return color;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[Color boringColor];
[Color boringColor];
[Color boringColor];
}
2014-03-23 14:01:30.653 once_t[1215:60b] 0
2014-03-23 14:01:30.655 once_t[1215:60b] dispatch_once
2014-03-23 14:01:30.655 once_t[1215:60b] -1
2014-03-23 14:01:30.655 once_t[1215:60b] --------------
2014-03-23 14:01:30.656 once_t[1215:60b] -1
2014-03-23 14:01:30.656 once_t[1215:60b] -1
2014-03-23 14:01:30.656 once_t[1215:60b] --------------
2014-03-23 14:01:30.656 once_t[1215:60b] -1
2014-03-23 14:01:30.657 once_t[1215:60b] -1
2014-03-23 14:01:30.657 once_t[1215:60b] --------------
下面咱們修改一下:
[objc] view plaincopy
static dispatch_once_t onceToken = 1L;
若是將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,另外不要隨意改變其值(不要將其設置爲類的屬性,實例變量等),不然會致使沒法預見的行爲。