dispatch_once 详解

dispatch_once 详解

  • [dispatch_once 详解](#dispatch_once 详解)

dispatch_once 详解

我们常用dispatch_once创建单例对象,先创建一个静态变量onceToken,调用dispatch_once函数将onceToken和block做参数传入,在block完成静态对象变量instance的初始化。

objc 复制代码
+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    static id instance = nil;
    dispatch_once(&onceToken, ^{
        instance = [[NSObject alloc] init];
    });
    return instance;
}

dispatch_once的源码:

objc 复制代码
void dispatch_once(dispatch_once_t *val, dispatch_block_t block)
{
    dispatch_once_f(val, block, _dispatch_Block_invoke(block));
}
// 实现代码在下面
DISPATCH_NOINLINE
void dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
    dispatch_once_gate_t l = (dispatch_once_gate_t)val;

#if !DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER
    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);
}

通过包装token作为唯一标识,判断(v == DLOCK_ONCE_DONE)是否已经完成初始化,实际就是判断是否调用过block(func内部调用)。如果已经初始化完成,那么v == DLOCK_ONCE_DONE,直接返回;如果还未初始化完成则往下走。

如果没有初始化,这里进来会先通过token获取锁。如果锁已被占用,说明其他线程在初始化,往下进入等待状态;如果获取锁成功则通过_dispatch_once_callout调用block,进行block回调:

objc 复制代码
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);
}

DISPATCH_ALWAYS_INLINE
static inline void _dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
    return f(ctxt);
}

block调用完成之后通过_dispatch_once_gate_broadcast广播通知初始化完成,把v标记为DLOCK_ONCE_DONE,并释放锁。

如果block正在调用,有其他线程进来,这时候它们尝试获取锁失败,进入等待状态。等到第一次进来初始化完成之后发出广播,它们收到消息立即返回。

总结:dispatch_once首先通过onceToken作为一个唯一标识用来判断对象是否已经初始化(实际就是block有没有执行完成)。然后在第一次调用block(实际上就是初始化对象的时候)加锁,防止其他线程进入。其他线程进来时发现锁被占用就会进入等待状态。当第一次调用block初始化完成之后,就会把onceToken标记的值更新未初始化,然后释放锁,同时会发出通知,让其他等待的线程返回。当二次或多次调用dispatch_once的时候,判断对象已经被初始化,就直接返回,不会再调用block。

相关推荐
用户794572239541330 分钟前
【SwiftyJSON】拯救你的 as? [String: Any]——链式 JSON 访问的正确姿势
swiftui·objective-c·swift
冰凌时空33 分钟前
Swift vs Objective-C:语言设计哲学的全面对比
ios·openai
用户794572239541333 分钟前
【Moya】为什么你的 Alamofire 代码需要再封装一层?
swiftui·objective-c·swift
花间相见2 小时前
【大模型微调与部署03】—— ms-swift-3.12 命令行参数(训练、推理、对齐、量化、部署全参数)
开发语言·ios·swift
SameX2 小时前
删掉ML推荐、砍掉五时段分析——做专注App时我三次推翻自己,换来了什么
ios
YJlio3 小时前
2026年4月19日60秒读懂世界:从学位扩容到人形机器人夺冠,今天最值得关注的6个信号
python·安全·ios·机器人·word·iphone·7-zip
90后的晨仔16 小时前
《SwiftUI 高级特性第1章:自定义视图》
ios
空中海17 小时前
第二章:SwiftUI 视图基础
ios·swiftui·swift
空中海19 小时前
第七章:iOS网络与数据持久化
网络·ios
空中海20 小时前
第六章:iOS导航与路由系统
macos·ios·cocoa