# 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](/files/-MiqFhClWuMZGUhlrXd7)

入参：

* 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](/files/-MiqFhCptWsAV2rr-4MF)

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

![3](/files/-MiqFhCrUwQPeUzQjuRc)

## 参考

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ryukiedev.gitbook.io/wiki/ios/di-ceng/25.dispatchonce-dan-li-shi-xian-yuan-li.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
