GCD还提供单次初始化支持,这个与pthread中的函数 pthread_once 很相似。GCD提供的方式的优点在于它使用block而非函数指针,这就允许更自然的代码方式。
这个特性的主要用途是惰性单例初始化或者其他的线程安全数据共享。典型的单例初始化技术看起来像这样(线程安全的):
01
02
03
04
05
06
07
08
09
10
|
+
(id)sharedWhatever { static Whatever
*whatever = nil; @synchronized([Whatever class ]) { if (!whatever) whatever
= [[Whatever alloc] init]; } return whatever; } |
这挺好的,但是代价比较昂贵;每次调用 +sharedWhatever 函数都会付出取锁的代价,即使这个锁只需要进行一次。确实有更风骚的方式来实现这个,使用类似双向锁或者是原子操作的东西,但是这样挺难弄而且容易出错。创建单例有更加简单的方式,使用GCD,我们可以这样重写上面的方法,使用函数 dispatch_once:
1
2
3
4
5
6
7
8
9
|
+
(id)sharedWhatever { static dispatch_once_t
pred; static Whatever
*whatever = nil; dispatch_once(&pred,
^{ whatever
= [[Whatever alloc] init]; }); return whatever; } |
该方法有很多优势:
1 线程安全
2 很好满足静态分析器要求
3 和自动引用计数(ARC)兼容
4 仅需要少量代码
这个稍微比 @synchronized方法简单些,并且GCD确保以更快的方式完成这些检测,它保证block中的代码在任何线程通过 dispatch_once 调用之前被执行,但它不会强制每次调用这个函数都让代码进行同步控制。实际上,如果你去看这个函数所在的头文件,你会发现目前它的实现其实是一个宏,进行了内联的初始化测试,这意味着通常情况下,你不用付出函数调用的负载代价,并且会有更少的同步控制负载。