iOS 的單例模式 dispatch_once
iOS 的單例模式 dispatch_once
有些變數只需要初始化一次(如從檔案中讀取配置參數,讀取裝置型號等等),可以使用dispatch_once來進行讀取最佳化,保證只調用API一次,以後就只要直接存取變數即可.
WeatherClient.h
#import "AFHTTPClient.h"
@interface WeatherClient : AFHTTPClient
+(WeatherClient *)sharedClient;
@end
WeatherClient.m
#import "WeatherClient.h"
@implementation WeatherClient
+(WeatherClient *)sharedClient
{
static WeatherClient *sharedClient=nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{sharedClient =[[WeatherClient alloc] initWithBaseURL:[NSURL URLWithString:API]];
});
return sharedClient;
}
@end
在軟體工程中,單例是一種用於實現單例的數學概念,即將類的執行個體化限制成僅一個對象的設計模式。
或者我的理解是:
單例是一種類,該類只能執行個體化一個對象。
儘管這是單例的實際定義,但在Foundation架構中不一定是這樣。比如NSFileManger和NSNotificationCenter,分別通過它們的類方法defaultManager和defaultCenter擷取。儘管不是嚴格意義的單例,這些類方法返回一個可以在應用的所有代碼中訪問到的類的共用執行個體。在本文中我們也會採用該方法。 使用Objective-C實現單例模式的最佳方式向來有很多爭論,開發人員(包括Apple在內)似乎每幾年就會改變他們的想法。當Apple引入了Grand Central Dispatch (GCD)(Mac OS 10.6和iOS4.0),他們也引入了一個很適合用於實現單例模式的函數。 該函數就是dispatch_once:
void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);
該函數接收一個dispatch_once用於檢查該代碼塊是否已經被調度的謂詞(是一個長整型,實際上作為BOOL使用)。它還接收一個希望在應用的生命週期內僅被調度一次的代碼塊,對於本例就用於shared執行個體的執行個體化。dispatch_once不僅意味著代碼僅會被運行一次,而且還是安全執行緒的,這就意味著你不需要使用諸如@synchronized之類的來防止使用多個線程或者隊列時不同步的問題。 Apple的GCD Documentation證實了這一點:
如果被多個線程調用,該函數會同步等等直至代碼塊完成。
實際要如何使用這些呢? 好吧,假設有一個AccountManager類,你想在整個應用中訪問該類的共用執行個體。你可以按如下代碼簡單實現一個類方法:
+ (AccountManager *)sharedManager {
static AccountManager *sharedAccountManagerInstance = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedAccountManagerInstance = [[self alloc] init];
});
return sharedAccountManagerInstance;
}
這就意味著你任何時候訪問共用執行個體,需要做的僅是:
AccountManager *accountManager = [AccountManager sharedManager];
就這些,你現在在應用中就有一個共用的執行個體,該執行個體只會被建立一次。 該方法有很多優勢:
1 安全執行緒 2 很好滿足靜態分析器要求 3 和自動引用計數(ARC)相容
4 僅需要少量代碼 該方法的劣勢就是它仍然運行建立一個非共用的執行個體:
AccountManager *accountManager = [[AccountManager alloc] init];
有些時候你希望有這種行為,但如果正在想要的是僅一個執行個體被執行個體化就需要注意這點。