# 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](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2F37dd6368f608a6d6ae928e632e1872292577e5da.png?generation=1630850448171038\&alt=media)

入参：

* dispatch\_once\_t
  * 一个静态标记
* dispatch\_block\_t
  * 执行的 block

### 1.1 dispatch\_once\_f

* 对 `static dispatch_once_t onceToken` 进行类型转换
* 通过 `onceToken` 判断是否第一次执行

```cpp
 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***

这里尝试进入，原子性的操作，线程安全。锁在当前的线程空间。

```cpp
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

第一次执行的相关操作

```cpp
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

```cpp
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](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2F843ab4e53a51685bd5069ad1037faaa375a487d9.png?generation=1630850447157416\&alt=media)

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

![3](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2F20d31e0c0fbd23a1377131326c4db451677ad43a.png?generation=1630850449191258\&alt=media)

## 参考

[GCD源码：libdispatch](https://opensource.apple.com/tarballs/libdispatch/)
