單例模式(singleton)對Objective-C開發(fā)者來說并不陌生瓷患,常見的實現(xiàn)方式為: 在類中編寫名為sharedInstance的方法,該方法只會返回全類共用的單例實例,而不會在每次調(diào)用時都創(chuàng)建新的實例末购。假設有個類叫做EOCClass,那么這個共享實例的方法一般都會這樣寫:
@implementation EOCClass
+ (id)sharedInstance
{
static EOCClass *sharedInstance = nil;
@synchronized (self) {
if (!sharedInstance) {
sharedInstance = [[self alloc] init];
}
}
return sharedInstance;
}
@end
線程安全是爭論的主要問題。為保證線程安全佩迟,上述代碼將創(chuàng)建單例實例的代碼包裹在同步塊里团滥。無論是好是壞,反正這種實現(xiàn)方式很常用报强。
不過GCD引入了一項特性灸姊,能使單例實現(xiàn)起來更為容易。所用的函數(shù)是:
void dispatch_once(dispatch_once_t *token,
dispatch_block_t block);
此函數(shù)接受類型為dispatch_once_t的特殊參數(shù)躺涝,筆者稱其為"標記"(token)厨钻,此外還接受塊參數(shù)。對于給定的標記來說坚嗜,該函數(shù)保證相關的塊必定會執(zhí)行夯膀,且僅執(zhí)行一次。首次調(diào)用該函數(shù)時苍蔬,必然會執(zhí)行塊中的代碼诱建,最重要的一點在于,此操作完全是線程安全的碟绑。對于只需執(zhí)行一次的塊來說俺猿,每次調(diào)用函數(shù)時傳入的標記都必須完全相同。因此格仲,開發(fā)者通常將標記變量聲明在static或global作用域里押袍。
剛才實現(xiàn)單例模式所用的sharedInstance方法,可以用此函數(shù)來改寫:
+ (id)sharedInstance {
static EOCClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
使用dispatch_once可以簡化代碼并且徹底保證線程安全凯肋,開發(fā)者根本無須擔心加鎖或同步谊惭。所有問題都由GCD在底層處理。由于每次調(diào)用時都必須使用完全相同的標記侮东,所以標記要聲明成static圈盔。把該變量定義在static作用域中,可以保證編譯器在每次執(zhí)行sharedInstance方法時都會復用這個變量悄雅,而不會創(chuàng)建新變量驱敲。
此外,dispatch_once更高效宽闲。它沒有使用重量級的同步機制众眨,若是那樣做的話,每次運行代碼前都要獲取鎖容诬,相反围辙,此函數(shù)采用"原子訪問"(atomic access)來查詢標記,以判斷其所對應的代碼原來是否已經(jīng)執(zhí)行過放案。