dispatch_once_t详解

概览

typedef intptr_t dispatch_once_t;

定义在once.h中,整个api很少,实现直接内联在头文件,如下

void
dispatch_once(dispatch_once_t *predicate,
        DISPATCH_NOESCAPE dispatch_block_t block);
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
#endif // DISPATCH_ONCE_INLINE_FASTPATH

如上就是我们常用的dispatch_once,通过block实现,我们调用的dispatch_once实际上调用的是_dispatch_once函数

void
dispatch_once_f(dispatch_once_t *predicate, void *_Nullable context,
        dispatch_function_t function);
void
_dispatch_once_f(dispatch_once_t *predicate, void *_Nullable context,
        dispatch_function_t function)
{
    if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
        dispatch_once_f(predicate, context, function);
    } else {
        dispatch_compiler_barrier();
    }
    DISPATCH_COMPILER_CAN_ASSUME(*predicate == ~0l);
}
#undef dispatch_once_f
#define dispatch_once_f _dispatch_once_f
#endif // DISPATCH_ONCE_INLINE_FASTPATH

如上是通过c函数指针实现,我们调用的dispatch_once_f实际上调用的是_dispatch_once_f函数

使用场景

像这样,写一个单例

+ (instancetype)sharedInstance {
    static Person *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

或者像这样,控制页面对象生命周期只触发一次的操作

@property (nonatomic, assign) dispatch_once_t onceToken;

dispatch_once(&_onceToken, ^{
    // do something
});

原理

+ (instancetype)sharedInstance {
    static Person *sharedInstance = nil;
    static dispatch_once_t onceToken;
    NSLog(@"%li", onceToken);
    dispatch_once(&onceToken, ^{
        NSLog(@"%li", onceToken);
        sharedInstance = [[self alloc] init];
        NSLog(@"%li", onceToken);
    });
    NSLog(@"%li", onceToken);
    return sharedInstance;
}
打印:
2021-08-23 13:05:47.412501+0800 GCDDemo[4517:2524707] 0
2021-08-23 13:05:47.412585+0800 GCDDemo[4517:2524707] 256
2021-08-23 13:05:47.412621+0800 GCDDemo[4517:2524707] 256
2021-08-23 13:05:47.412647+0800 GCDDemo[4517:2524707] -1

两个static关键字控制变量只初始化一次,存储在静态区,&是因为函数参数定义是dispatch_once_t *predicate指针,取地址操作用作引用传递,dispatch_once用来控制sharedInstance指针只赋值一次

  • 1、onceToken初始值是0,也只有是0才会正常执行,dispatch_once源码实现,0进入block
  • 2、进入块之后添加信号量来保证原子操作,之后将onceToken值置为一个正整数,用来标识正在执行
  • 3、执行完毕后onceToken值置为-1,用来标识已执行过,下次执行直接跳过

你可能感兴趣的:(dispatch_once_t详解)