dispatch_once 在单例模式可以保证代码被执行一次。
+ (MyObject *)sharedInstance
{
static MyObject * instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[MyObject alloc] init];
});
return instance;
}
dispatch_once_t 在 usr/include/dispatch/once.h 文件中定义:
/*!
* @typedef dispatch_once_t
*
* @abstract
* A predicate for use with dispatch_once(). It must be initialized to zero.
* Note: static and global variables default to zero.
*/
typedef long dispatch_once_t;
dispatch_once 展开:
#define dispatch_once _dispatch_once
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);
}
~0l 是 long 的 0 取反,结果是 一大堆 1。
再展开 DISPATCH_EXPECT
#if __GNUC__
#define DISPATCH_EXPECT(x, v) __builtin_expect((x), (v))
#else
#define DISPATCH_EXPECT(x, v) (x)
#endif
__builtin_expect 是 GCC(version >= 2.9) 引进的宏,其作用就是帮助编译器判断条件跳转的预期值,避免跳转造成时间浪费。并没有改变其对真值的判断。
所以 dispatch_once 可以看成
+ (MyObject *)sharedInstance
{
static MyObject * instance;
static long onceToken = 0;
if (onceToken != 0){
1...
{
instance = [[MyObject alloc] init];
}
2...
}
return format;
}
我们可以猜测在下面的 2... 里的代码是修改了 onceToken 的值。
输出查看一下:
+ (MyObject *)sharedInstance
{
static MyObject * instance;
static dispatch_once_t onceToken;
NSLog(@"##111## %ld", onceToken);
dispatch_once(&onceToken, ^{
NSLog(@"##222## %ld", onceToken);
instance = [[MyObject alloc] init];
});
NSLog(@"##333## %ld", onceToken);
return instance;
}
2018-05-04 16:34:17.486360+0800 Demo[27355:1941829] ##111## 0
2018-05-04 16:34:17.486527+0800 Demo[27355:1941829] ##222## 768
2018-05-04 16:34:17.486637+0800 Demo[27355:1941829] ##333## -1
2018-05-04 16:34:17.486730+0800 Demo[27355:1941829] ##111## -1
2018-05-04 16:34:17.486843+0800 Demo[27355:1941829] ##333## -1
发现在 1 里改变了一次,然后在 2 里改成了 -1。这样我们就不难理解 dispatch_once 的逻辑了。
dispatch_once 实现单例的优点:
①、线程安全
②、很好满足静态分析器要求
③、和自动引用计数(ARC)兼容
④、仅需要少量代码
网友评论