概览
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,用来标识已执行过,下次执行直接跳过