dispatch_once深入

2018-03-28  本文已影响47人  nadou23

1.dispatch_once是用来进行一次初始化的通常我们使用dispatch_once时都是这样的

static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
         //do something
    });

2.首先要了解到dispatch_once_t是个long类型,并且编译器初始化onceToken为0,看源代码也可以知道

typedef long dispatch_once_t;
// A predicate for use with dispatch_once(). It must be initialized to zero.

3.而对于 dispatch_once进入它的头文件发现

#ifdef __BLOCKS__
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
DISPATCH_SWIFT3_UNAVAILABLE("Use lazily initialized globals instead")
void
dispatch_once(dispatch_once_t *predicate,
        DISPATCH_NOESCAPE dispatch_block_t block);

DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
DISPATCH_SWIFT3_UNAVAILABLE("Use lazily initialized globals instead")
void
_dispatch_once(dispatch_once_t *predicate,
        DISPATCH_NOESCAPE dispatch_block_t block)
{
    if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
        dispatch_once(predicate, block);
    } else {
        dispatch_compiler_barrier();
    }
    DISPATCH_COMPILER_CAN_ASSUME(*predicate == ~0l);
}
#undef dispatch_once
#define dispatch_once _dispatch_once
#endif


4.其中可以发现其实 dispatch_once是_dispatch_once函数的宏,我们调用dispatch_once实际上是首先调用了_dispatch_once函数,dispatch_once函数内部大概意思就是判断DISPATCH_EXPECT(predicate, ~0l) != ~0l是否不是 -1(~0l 表示长整型0按位取反,其实就是长整型的-1),如果确实不是-1,那就执行dispatch_once(predicate, block),如果是-1,那就执行dispatch_compiler_barrier()。
5.当然对DISPATCH_EXPECT(
predicate, ~0l)会有疑惑,点进去看它的定义,可以看到

#if __GNUC__
#define DISPATCH_EXPECT(x, v) __builtin_expect((x), (v))
#define dispatch_compiler_barrier()  __asm__ __volatile__("" ::: "memory")
#else
#define DISPATCH_EXPECT(x, v) (x)
#define dispatch_compiler_barrier()  do { } while (0)
#endif

从上面的判断GNUC(就是判断gcc版本的预定义宏,现在一般都是满足条件的)可以看到#define DISPATCH_EXPECT(x, v) __builtin_expect((x), (v)),它是__builtin_expect((x), (v))的宏,而对于__builtin_expect((x), (v)

__builtin_expect(onceToken, ~0l) 当onceToken == 0时,DISPATCH_EXPECT(*predicate, ~0l) != ~0l的结果就是TRUE,执行dispatch_once(predicate, block),同时dispatch_once执行完后,onceToken就变为-1了,当你再一次从最开始执行dispatch_once时,就执行dispatch_compiler_barrier()。
上一篇下一篇

猜你喜欢

热点阅读