339 字
2 分钟
dispatch_once单例实现原理
前言
日常使用单例的时候会这样用,但是你有考虑过内部具体是怎么实现的么?本文将带你通过源码探究 dispatch_once 的实现原理。
+ (instancetype)shared { static RYModel *object; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ object = [[RYModel alloc] init]; }); return object;}一、 dispatch_once 源码解析

入参:
- dispatch_once_t
- 一个静态标记
- dispatch_block_t
- 执行的 block
1.1 dispatch_once_f
- 对
static dispatch_once_t onceToken进行类型转换 - 通过
onceToken判断是否第一次执行
DISPATCH_NOINLINEvoiddispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func){ // onceToken 全局静态变量类型转换 dispatch_once_gate_t l = (dispatch_once_gate_t)val;
#if !DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER // 通过 onceToken uintptr_t v = os_atomic_load(&l->dgo_once, acquire); if (likely(v == DLOCK_ONCE_DONE)) { // 已经执行过了 return; }#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER if (likely(DISPATCH_ONCE_IS_GEN(v))) { return _dispatch_once_mark_done_if_quiesced(l, v); }#endif#endif if (_dispatch_once_gate_tryenter(l)) {// 判断是否可以执行(原子性) // 第一次执行 return _dispatch_once_callout(l, ctxt, func); } return _dispatch_once_wait(l);}_dispatch_once_gate_tryenter
这里尝试进入,原子性的操作,线程安全。锁在当前的线程空间。
DISPATCH_ALWAYS_INLINEstatic inline bool_dispatch_once_gate_tryenter(dispatch_once_gate_t l){ return os_atomic_cmpxchg(&l->dgo_once, DLOCK_ONCE_UNLOCKED, (uintptr_t)_dispatch_lock_value_for_self(), relaxed);}1.2 _dispatch_once_callout
第一次执行的相关操作
DISPATCH_NOINLINEstatic void_dispatch_once_callout(dispatch_once_gate_t l, void *ctxt, dispatch_function_t func){ // 执行任务 _dispatch_client_callout(ctxt, func); // 广播通知执行完了 _dispatch_once_gate_broadcast(l);}1.3 _dispatch_once_gate_broadcast
DISPATCH_ALWAYS_INLINEstatic inline void_dispatch_once_gate_broadcast(dispatch_once_gate_t l){ dispatch_lock value_self = _dispatch_lock_value_for_self(); uintptr_t v;#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER v = _dispatch_once_mark_quiescing(l);#else v = _dispatch_once_mark_done(l);#endif if (likely((dispatch_lock)v == value_self)) return; _dispatch_gate_broadcast_slow(&l->dgo_gate, (dispatch_lock)v);}_dispatch_once_mark_done 改变标识

回头看一下 dispatch_once_f 执行过就会被直接 return 了。
