1. 利用@synchronized創(chuàng)建單例
@implementation Singleton
+ (instancetype)shareInstance
{
static Singleton* single;
@synchronized(self){
if (!single) {
single = [[Singleton alloc] init];
}
}
return single;
}
@end
ps:嚴(yán)格意義上來說,我們還需要將alloc方法封住,因?yàn)閲?yán)格的單例是不允許再創(chuàng)建其他實(shí)例的,而alloc方法可以在外部任意生成實(shí)例。但是考慮到alloc屬于NSObject,iOS中無法將alloc變成私有方法,最多只能覆蓋alloc讓其返回空,不過這樣做也可能會(huì)讓使用接口的人誤解,造成其他問題。所以我們一般情況下對alloc不做特殊處理。系統(tǒng)的單例也未對alloc做任何處理
2. 利用dispatch_once創(chuàng)建單例
使用@synchronized雖然解決了多線程的問題,但是并不完美。因?yàn)橹挥性趕ingle未創(chuàng)建時(shí),我們加鎖才是有必要的。如果single已經(jīng)創(chuàng)建.這時(shí)候鎖不僅沒有好處,而且還會(huì)影響到程序執(zhí)行的性能(多個(gè)線程執(zhí)行@synchronized中的代碼時(shí),只有一個(gè)線程執(zhí)行,其他線程需要等待)。那么有沒有方法既可以解決問題,又不影響性能呢?
這個(gè)方法就是GCD中的dispatch_once
+ (instancetype)sharedInstance {
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
2.1 dispatch_once原理
dispatch_once為什么能做到既解決同步多線程問題又不影響性能呢?
下面我們來看看dispatch_once的原理:
dispatch_once主要是根據(jù)onceToken的值來決定怎么去執(zhí)行代碼。
- 當(dāng)onceToken = 0時(shí),線程執(zhí)行dispatch_once的block中代碼
- 當(dāng)onceToken = -1時(shí),線程跳過dispatch_once的block中代碼不執(zhí)行
- 當(dāng)onceToken為其他值時(shí),線程被線程被阻塞,等待onceToken值改變
當(dāng)線程首先調(diào)用shareInstance,某一線程要執(zhí)行block中的代碼時(shí),首先需要改變onceToken的值,再去執(zhí)行block中的代碼。這里onceToken的值變?yōu)榱?40734731430192。
這樣當(dāng)其他線程再獲取onceToken的值時(shí),值已經(jīng)變?yōu)?40734731430192。其他線程被阻塞。
當(dāng)block線程執(zhí)行完block之后。onceToken變?yōu)?1。其他線程不再阻塞,跳過block。
下次再調(diào)用shareInstance時(shí),block已經(jīng)為-1。直接跳過block。
這樣dispatch_once在首次調(diào)用時(shí)同步阻塞線程,生成單例之后,不再阻塞線程。dispatch_once是創(chuàng)建單例的最優(yōu)方案
參考
iOS 單例模式: http://www.itdecent.cn/p/a92c0283f243