美文网首页
iOS中的单利-dispatch_once

iOS中的单利-dispatch_once

作者: 希尔罗斯沃德_董 | 来源:发表于2021-08-21 21:17 被阅读0次

dispatch_once的使用

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

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

为什么这样就能创造出单利呢?单利对象的特点是全局唯一,那这就意味者这个对象这个对象只能被创建一次。dispatch_once是如何保证instance只创建一次的?

dispatch_once的底层原理

通过源码分析dispatch_once
void
dispatch_once(dispatch_once_t *val, dispatch_block_t block)
{
    dispatch_once_f(val, block, _dispatch_Block_invoke(block));
}

实现代码在dispatch_once_f内:

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);
}
onceToken的地址(val)作为唯一标识

当调用dispatch_once会通过包装token作为唯一标识,判断(v == DLOCK_ONCE_DONE)是否已经完成初始化,实际就是判断是否调用过block(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;
    }

如果已经初始化完成,那么v == DLOCK_ONCE_DONE,直接返回;如果还未初始化完成则往下走。

block调用

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

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正在调用,有其他线程进来,这时候他们尝试获取锁失败,进入等待状态:

return _dispatch_once_wait(l);

等到第一次进来初始化完成之后发出广播,它们收到消息立即返回。

总结

dispatch_once是通过onceToken静态变量地址作为唯一标识,防止重复创建;同时在block调用时进行加锁,防止多线程同时创建。两个结合保证了block只调用一次,保证单利对象instance的唯一性。

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

相关文章

  • iOS中的单利-dispatch_once

    dispatch_once的使用 我们常用dispatch_once创建单利对象,先创建一个静态变量onceTok...

  • GCD基础总结二

    上代码~ barrier的运用 打印结果 延时执行 dispatch_once,一般创建单利可以使用 dispat...

  • iOS笔记之_单例

    iOS 单例 dispatch_once函数 dispatch_once函数中有两个参数,对于给定的一个predi...

  • iOS通知的简单使用

    NSNotificationCenter NSNotification是iOS中的一个调度消息通知的类,采用单利模...

  • iOS单利

    ARC环境 懒汉式 .h文件 .m文件 饿汉式 饿汉式是让程序加载所有资源时就保证这个单利存在内存之中,所以此时不...

  • iOS SQLite(数据库)

    iOS - SQL 1>单利写法创建方式 只有一个对象// 单利不能够被释放哦,内存管理中要使用数据库第一步要导入...

  • iOS单利的销毁

    iOS创建单利对象 直到程序kill的时候才会释放 如果滥用他会一直占用内存 当然也可以销毁单利对象

  • iOS 单利的使用

    https://www.cnblogs.com/JackieHoo/p/5050010.html

  • iOS-单利

    .h类 .m类

  • iOS 单利模式

    单利模式 单例模式的作用 可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问从而方便地控制了实例...

网友评论

      本文标题:iOS中的单利-dispatch_once

      本文链接:https://www.haomeiwen.com/subject/alqabltx.html