美文网首页
dispatch_once

dispatch_once

作者: 和风细羽 | 来源:发表于2018-11-05 22:55 被阅读0次

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)兼容
  ④、仅需要少量代码

相关文章

网友评论

      本文标题:dispatch_once

      本文链接:https://www.haomeiwen.com/subject/xugexqtx.html