美文网首页
『ios』关于单例 dispatch_once原理的学习

『ios』关于单例 dispatch_once原理的学习

作者: butterflyer | 来源:发表于2021-03-08 18:28 被阅读0次

    dispatch_once我们平时经常用吧,特别是创建单例的时候,但是可能我们平时只是在于用它,并不知道他是如何实现的。
    我也在思考这个问题。

    +(instancetype)shareInstance{
        static ServiceApp *service = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            service = [[ServiceApp alloc]init];
        });
        return service;
    }
    

    单例是如何只执行一次的呢?
    带着这个问题,我们往下看。
    这个函数的基本内部构成,我们主要看这个函数dispatch_once_f。

    #ifdef __BLOCKS__
    __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
    DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
    void
    dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
    
    //注意这个内联函数
    DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
    void
    _dispatch_once(dispatch_once_t *predicate, dispatch_block_t block)
    {
        if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
            dispatch_once(predicate, block);
        }
    }
    #undef dispatch_once
    #define dispatch_once _dispatch_once
    #endif
    __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
    DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW
    void
    dispatch_once_f(dispatch_once_t *predicate, void *context,
            dispatch_function_t function);
    
    DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_NONNULL1 DISPATCH_NONNULL3
    DISPATCH_NOTHROW
    void
    _dispatch_once_f(dispatch_once_t *predicate, void *context,
            dispatch_function_t function)
    {
        if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
            dispatch_once_f(predicate, context, function);
        }
    }
    #undef dispatch_once_f
    #define dispatch_once_f _dispatch_once_f
    
    __END_DECLS
    
    #endif
    

    下面我们来看dispatch_once_f。

    
    dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
    {
    //结构体
    dispatch_once_gate_t l = (dispatch_once_gate_t)val;
     //这里我们放到后面去看,请记住DLOCK_ONCE_DONE这个东西
    #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);
    
    

    通过查阅资料,dispatch_once_gate_t是一个结构体。
    我们来看这个函数。

    if (_dispatch_once_gate_tryenter(l)) {
            return _dispatch_once_callout(l, ctxt, func);
        }
    
    _dispatch_once_gate_tryenter(dispatch_once_gate_t l)
    {
        // os 对象是否存储过
        // unlock
        return os_atomic_cmpxchg(&l->dgo_once, DLOCK_ONCE_UNLOCKED,
                (uintptr_t)_dispatch_lock_value_for_self(), relaxed);
    }
    
    

    dispatch_once_callout是执行dispatch_once block里的函数。

    _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_client_callout这个函数,就执行下面的广播函数_dispatch_once_gate_broadcast(l) 。

    
    _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_quiescing和_dispatch_once_mark_done这两个地方。

    
    _dispatch_once_mark_quiescing(dispatch_once_gate_t dgo)
    {
        return os_atomic_xchg(&dgo->dgo_once, _dispatch_once_generation(), release);
    }
    

    _dispatch_once_mark_quiescing标记着正在创建。

    
    _dispatch_once_mark_done(dispatch_once_gate_t dgo)
    {
        return os_atomic_xchg(&dgo->dgo_once, DLOCK_ONCE_DONE, release);
    }
    

    _dispatch_once_mark_done标识结束,并标记为DLOCK_ONCE_DONE。

    
    _dispatch_once_mark_done(dispatch_once_gate_t dgo)
    {
        return os_atomic_xchg(&dgo->dgo_once, DLOCK_ONCE_DONE, release);
    }
    

    在这里我们可以回头看,最开始dispatch_once_f中的代码。

    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);
        }
    

    相信看到DLOCK_ONCE_DONE,就应该懂了,是什么。如果检测到已经执行过,就直接返回。
    如果是正在创建,就执行_dispatch_once_mark_done_if_quiesced,并标记为DLOCK_ONCE_DONE。

    
    _dispatch_once_mark_done_if_quiesced(dispatch_once_gate_t dgo, uintptr_t gen)
    {
        if (_dispatch_once_generation() - gen >= DISPATCH_ONCE_GEN_SAFE_DELTA) {
            /*
             * See explanation above, when the quiescing counter approach is taken
             * then this store needs only to be relaxed as it is used as a witness
             * that the required barriers have happened.
             */
            os_atomic_store(&dgo->dgo_once, DLOCK_ONCE_DONE, relaxed);
        }
    }
    

    dispatch_once的原理大概就是这样了。为什么只执行一次。
    主要在DLOCK_ONCE_DONE这个标记。

    相关文章

      网友评论

          本文标题:『ios』关于单例 dispatch_once原理的学习

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