ios建立單例中的@synchronized和dispatch_once
@synchronized和dispatch_once 在單例的使用如下:
static LvSingleClass * singleClass = nil; + (LvSingleClass *)sharedSingleClass { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ singleClass = [[LvSingleClass alloc] init]; }); return singleClass; } + (LvSingleClass *)sharedSingleClass { @synchronized(self){ if (singleClass == nil) { singleClass = [[LvSingleClass alloc] init]; } } return singleClass; }
在OC中,@synchronize 是用NSRecursiveLock實現的,並且隱式添加了exception handler,如果有異常丟擲,exception handler 會自動釋放互斥鎖。
而dispatch_once 它省去了鎖的操作,用的是大量的原子操作,該原子操作內部不是靠pthread等鎖來實現的,而是利用lock的彙編指令,靠底層cpu指令來執行的 ,所以擁有高的效能,
//程式碼測試
CFAbsoluteTime dispatchStartTime = CFAbsoluteTimeGetCurrent(); for (int i = 0;i < 1000;i ++) { [SingleClass sharedSingle]; } CFAbsoluteTime dispatchEndTime = CFAbsoluteTimeGetCurrent(); NSLog(@"dispatch time:%f s",dispatchEndTime - dispatchStartTime); CFAbsoluteTime synchronizeStartTime = CFAbsoluteTimeGetCurrent(); for (int i = 0;i < 1000 ;i++) { [SingleOneClass sharedSingle]; } CFAbsoluteTime synchronizedEndTime = CFAbsoluteTimeGetCurrent(); NSLog(@"synchronized time : %f s",synchronizedEndTime - synchronizeStartTime);
//測試結果
2018-10-31 14:49:22.313217+0800 Demo[20364:6801421] dispatch time:0.000047 s
2018-10-31 14:49:22.313530+0800 Demo[20364:6801421] synchronized time : 0.000116 s
可以發現 在單執行緒中可以發現dispatch_once方法的效能要明顯優於synchronized方法,所以在實際的應用中我們可以多采用dispatch_once方式來實現單例。
-
下面來簡單瞭解下 @synchronized
@synchronized指令的物件是用於區分受保護塊的唯一識別符號。如果在兩個不同的執行緒中執行上述方法,則anObj在每個執行緒上為引數傳遞一個不同的物件,每個執行緒都會鎖定並繼續處理,而不會被另一個阻塞。但是,如果在兩種情況下都傳遞相同的物件,則其中一個執行緒將首先獲取鎖定,另一個執行緒將阻塞,直到第一個執行緒完成關鍵部分。
作為預防措施,該@synchronized塊隱式地向受保護程式碼新增異常處理程式。如果丟擲異常,此處理程式會自動釋放互斥鎖。這意味著為了使用該@synchronized指令,還必須在程式碼中啟用Objective-C異常處理。如果您不希望由隱式異常處理程式引起額外開銷,則應考慮使用鎖類。 -
下面來簡單瞭解下 dispatch_once
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //some code });
onceToken 引數它保證了後面的 block 只被執行一遍。我們通過一個簡單的單例實現來看看 onceToken 在 dispatch_once 執行過程中的變化:
+ (SingleClass *)sharedSingle { static SingleClass * singleClass = nil; static dispatch_once_t onceToken; NSLog(@"Before onceToken = %ld", onceToken); dispatch_once(&onceToken, ^{ singleClass = [[SingleClass alloc] init]; NSLog(@"running onceToken = %ld", onceToken); }); NSLog(@"After onceToken = %ld", onceToken); return singleClass; }
執行結果
2018-10-31 16:05:05.306434+0800 Demo[20530:7311130] Before onceToken = 0 2018-10-31 16:05:05.306574+0800 Demo[20530:7311130] running onceToken = 1024 2018-10-31 16:05:05.306736+0800 Demo[20530:7311130] After onceToken = -1
通過輸出我們可以發現,在 dispatch_once 執行前,onceToken 的值是 0,因為 dispatch_once_t 是由 typedef long dispatch_once_t 而來,所以在 onceToken 還沒被手動賦值的情況下,0 是編譯器給 onceToken 的初始化賦值。在 dispatch_once 執行過程中,onceToken 是一個很大的數字,這個值是 dispath_once 內部實現中一個區域性變數的地址,並不是一個固定的值。當 dispatch_once 執行完畢,onceToken 的值被賦為 -1。