第四十五条:使用dispatch_once来执行只需运行一次的代码

2017/9/28 posted in  Effective OC2.0

单例模式顾名思义,就是一个类一般只会同时存在一个实例,常见的实现方式为在类中编写名为sharedInstance的类方法。例如:

+(id)sharedImstance{
    static EOCClass *sharedlnstance = nil;
        @synchronized(self){
            if (!sharedlnstance) {
                sharedlnstance = [[self alloc] init];
            }
    }
    return sharedlnstance;
}

该方法只会返回全类共用的单例实例,而不会在每次调用时都创建新的实例。

为保证线程安全,上述代码将创建单例实例的代码包裹在同步块里。

GCD中的一个函数可以更简单的执行这种只需执行一次的代码。为:

void dispatch_once (dispatch_once_t *token,
                    dispatch_block_t block);

此函数接受类型为的dispatch_once_t的特殊参数token,此外还接受块参数,在块里面执行我们所需要运行一次的代码。对于给定的token来说,该函数保证相关的块必定会执行,且仅执行一次。首次调用该函数时,必然会执行块中的代码,最重要的一点在于,此操作完全是线程安全的。请注意,对于只需执行一次的块来说,每次凋用函数时传入的标记都必须完全相同。因此,开发 者通常将标记变量声明在staticglobal作用域里。

将上面的代码改为dispatch_once来执行,就可以换为:

+ (id)sharedInstance {
    static EOCClass ^sharedInstance = nil; 
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedlnstance = [[self alloc] initJ;
    });
    return sharedInstance;
}

使用dispatch_once可以简化代码并且彻底保证线程安全,开发者根本无须担心加锁或同步。所有问题都由GCD在底层处理。

相比于同步机制的繁琐,dispatch_once更高效,此函数采用“原子访问”(atomic access)来査询token,以判断其所对应的代码原来是否已经执行过。速度一般是同步机制的两倍。

要点

  • 经常需要编写“只需执行一次的线程安全代码”(thread-safe single-code execution)。通过GCD所提供的dispatch_once函数,很容易就能实现此功能。
  • 标记应该声明在staticglobal作用域中,这样的话,在把只需执行一次的块传给dispatch once函数时,传进去的标记也是相同的。