Links

25.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 源码解析

01
入参:
  • dispatch_once_t
    • 一个静态标记
  • dispatch_block_t
    • 执行的 block

1.1 dispatch_once_f

  • static dispatch_once_t onceToken 进行类型转换
  • 通过 onceToken 判断是否第一次执行
DISPATCH_NOINLINE
void
dispatch_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_INLINE
static 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_NOINLINE
static 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_INLINE
static 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 改变标识
2
回头看一下 dispatch_once_f 执行过就会被直接 return 了。
3

参考