GCD里的單例函數(shù)dispatch_once是我們經(jīng)常會(huì)用到的,今天我們來(lái)稍做深入分析一下。GCD的源碼都在libdispatch.dylib庫(kù)里,這個(gè)庫(kù)在libSystem_initializer被初始化,可理解為在dyld里被加載和初始化的(之前的文章有分析過(guò))。dispatch_once作為單例的使用入口,通過(guò)分析得到它是一個(gè)宏定義,_dispatch_once函數(shù)在libdispatch.dylib可以查到。


_dispatch_once函數(shù)會(huì)調(diào)用dispatch_once。

最終來(lái)到dispatch_once_f,這里有實(shí)現(xiàn)單例函數(shù)的重要邏輯:

我們來(lái)分析下這個(gè)函數(shù),dispatch_once_t *val參數(shù)通常來(lái)自于外部傳來(lái)的靜態(tài)變量,靜態(tài)變量的唯一性。靜態(tài)值在函數(shù)內(nèi)部被轉(zhuǎn)換為l,通過(guò)獲取l的dgo_once屬性,判斷為DLOCK_ONCE_DONE,如果是則返回。下面我們重點(diǎn)看下第67行代碼,_dispatch_once_gate_tryenter函數(shù)。

_dispatch_once_gate_tryenter函數(shù)里,os_atomic_cmpxchg函數(shù)會(huì)對(duì)l->dgo_once和DLOCK_ONCE_UNLOCKED進(jìn)行比較,看是否相等,如果不是就上鎖_dispatch_lock_value_for_self,這樣操作也能防止多線程訪問(wèn)的不安全性和唯一性。此時(shí)來(lái)到dispatch_once_f函數(shù)的第68行_dispatch_once_callout函數(shù)。

這里的_dispatch_client_callout(ctxt, func)會(huì)對(duì)單例的block進(jìn)行調(diào)用。接下來(lái)_dispatch_once_gate_broadcast(l)會(huì)對(duì)外進(jìn)行“廣播”,可理解為一種標(biāo)識(shí),意思就是已經(jīng)執(zhí)行了一次block,不能再執(zhí)行了。

_dispatch_once_gate_broadcast函數(shù)里,(_dispatch_lock_value_for_self會(huì)進(jìn)行解鎖???)第670行_dispatch_once_mark_done函數(shù)。

_dispatch_once_mark_done函數(shù)會(huì)把dgo_once設(shè)置成DLOCK_ONCE_DONE!??!我們可以回到dispatch_once_f函數(shù)第58行可知,再下一次訪問(wèn)的時(shí)候,這里就會(huì)return,也就是通過(guò)上鎖達(dá)到了單例“執(zhí)行一次”的效果。當(dāng)多線程訪問(wèn)單例函數(shù)dispatch_once_f時(shí)候,會(huì)讓其在70行_dispatch_once_wait進(jìn)行等待,等到前面的線程訪問(wèn)結(jié)束,以保證線程安全。
PS:_dispatch_once_gate_tryenter函數(shù)進(jìn)行加鎖,_dispatch_once_gate_broadcast進(jìn)行解鎖。