標籤:
關鍵字:GCD dispatch_once 單例 安全執行緒 double-check
以前在Java、C#等語言中,我們實現遲緩載入的單例模式一般寫成如下形式(虛擬碼):
private MyClass() {...} // 私人化構造方法
private static MyClass instance; // 承載對象的變數
pubic static MyClass getInstance() { // 完成執行個體化任務
if (instance == null) { // 第一次判斷
lock (obj) { // 加鎖,處理多線程判斷
if (instance == null) {// 再次判斷,避免線程切換導致多個執行個體對象出現
instance = new MyClass(); // 完成最終執行個體化過程
}
}
}
return instance;
}
但是,在Objective-C中經常看到的卻是這種寫法:
+ (instancetype)sharedInstance {
static id sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [self new];
});
return sharedInstance;
}
這種寫法似乎並沒對多線程編程作防禦性處理。在sharedInstance = [self new];前後加入日誌輸出,可發現,即使在多線程環境下,dispatch_once也只執行一次。
其實,dispatch_once是安全執行緒的,即使在多個線程中同時調用,也只有一個塊被執行,其它dispatch_once塊的調用被阻塞,直到執行的那個塊運行結束,所以在整個程式運行周期內,dispatch_once塊只會運行一次,可以確定,下一行代碼執行前,整個dispatch_once塊是執行完畢的,不管當前背景工作執行緒是哪個。如果已執行,dispatch_once會被快速跳過,在類似迴圈體中調用這種場合,也無需擔心執行它的額外效能開銷。如果一個程式包含多個同一調用類的執行個體,只有其中一個執行個體會執行dispatch_once塊[1]。
參考:
[1]. Rob Napier 等著, 美團移動 譯. iOS編程實戰. 北京, 人民郵電出版社. 358~359頁
iOS 8:dispatch_once解惑