dispatch_once()工作原理

在iOS开发中,为保证单例在整个程序运行中只被初始化一次,单线程的时候,通过静态变量可以实现;但是多线程的出现,使得在极端条件下,单例也可能返回了不同的对象。如在单例初始化完成前,多个进程同时访问单例,那么这些进程可能都获得了不同的单例对象。苹果提供了 dispatch_once(dispatch_once_t *predicate,dispatch_block_t block);函数来避免这个问题。在实现单例类的时候,我们通常会下面的代码来初始化单例:

1
2
3
4
5
6
7
8
9
-(instancetype)sharedInstance
{
static SharedObject *sharedObject = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedObject = [[SharedObject alloc] init];
});
return sharedObject;
}

要理解dispatch_once()怎么确保单例类对象只被初始化一次,也就是要搞清楚dispatch_once()是怎么根据predicate指针来判断block是否执行完成的呢?

  • 首先,predicate指针所指向的变量的类型为dispatch_once_t,而dispatch_once_t变量初始化时必须设置为0
  • dispatch_once()在进行predicate(断言)判定时,dispatch_once_t变量的值只有为0时,才会执行block,因此初次调用时,block会被调用一次
  • 当block执行完毕后,系统会将dispatch_once_t变量的值置为-1,因此再次调用dispatch_once()的时候,就会跳过block
  • dispatch_once_t是一个只能为0或-1的Int值,默认为0,执行完block,被置为-1。若置为其他值,会报错EXC_BAD_ACCESS

这也就是dispatch_once()对block只执行一次的工作原理。