为啥苹果推荐使用 dispatch_once 来实现 ARC 下的单例模式?

Posted

技术标签:

【中文标题】为啥苹果推荐使用 dispatch_once 来实现 ARC 下的单例模式?【英文标题】:Why does Apple recommend to use dispatch_once for implementing the singleton pattern under ARC?为什么苹果推荐使用 dispatch_once 来实现 ARC 下的单例模式? 【发布时间】:2012-02-25 12:05:22 【问题描述】:

在 ARC 下单例的共享实例访问器中使用 dispatch_once 的确切原因是什么?

+ (MyClass *)sharedInstance

    //  Static local predicate must be initialized to 0
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken = 0;
    dispatch_once(&onceToken, ^
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    );
    return sharedInstance;

在后台异步实例化单例不是一个坏主意吗?我的意思是如果我请求该共享实例并立即依赖它会发生什么,但是 dispatch_once 需要到圣诞节才能创建我的对象?它不会立即返回对吗?至少这似乎是 Grand Central Dispatch 的重点。

那么他们为什么要这样做呢?

【问题讨论】:

Note: static and global variables default to zero. 【参考方案1】:

dispatch_once() 是绝对同步的。并非所有 GCD 方法都是异步执行的(例如,dispatch_sync() 是同步的)。使用dispatch_once() 替换以下成语:

+ (MyClass *)sharedInstance 
    static MyClass *sharedInstance = nil;
    @synchronized(self) 
        if (sharedInstance == nil) 
            sharedInstance = [[MyClass alloc] init];
        
    
    return sharedInstance;

dispatch_once() 的优势在于它更快。它在语义上也更清晰,因为它还可以保护您免受多个线程对您的 sharedInstance 执行 alloc init 的影响——如果它们都在同一确切时间尝试。它不允许创建两个实例。 dispatch_once() 的整个理念是“只做一次”,这正是我们正在做的事情。

【讨论】:

为了论证,我需要注意documentation 并没有说它正在同步执行。它只是说多个同时调用将被序列化。 您声称它更快 - 快多少?我没有理由认为你没有说实话,但我希望看到一个简单的基准。 我刚刚做了一个简单的基准测试(在 iPhone 5 上),看起来 dispatch_once 比 @synchronized 快大约 2 倍。 @ReneDohan:如果您 100% 确定没有人从不同的线程调用该方法,那么它可以工作。但是使用dispatch_once() 真的很简单(特别是因为Xcode 甚至会为你自动完成它成为一个完整的代码sn-p),这意味着你甚至不必考虑该方法是否需要是线程安全的。 @siuying:实际上,这不是真的。首先,在+initialize 中所做的任何事情都发生在类被触及之前,即使您还没有尝试创建共享实例。通常,延迟初始化(仅在需要时创建)更好。其次,即使您的绩效声明也不正确。 dispatch_once() 所花费的时间几乎与在 +initialize 中说 if (self == [MyClass class]) 所花费的时间相同。如果您已经有一个+initialize,那么在其中创建共享实例会更快,但大多数类都没有。【参考方案2】:

因为它只会运行一次。因此,如果您尝试从不同的线程访问它两次,它不会导致问题。

Mike Ash 在他的 Care and Feeding of Singletons 博文中有完整的描述。

并非所有 GCD 块都是异步运行的。

【讨论】:

Lily's 是一个更好的答案,但我要保留指向 Mike Ash 帖子的链接。

以上是关于为啥苹果推荐使用 dispatch_once 来实现 ARC 下的单例模式?的主要内容,如果未能解决你的问题,请参考以下文章

苹果osx怎么选择复制为啥直接复制到当前目录下了

为啥我的苹果11输入页面会自动乱跳?

为啥我的苹果电脑的文件不能拷贝到移动硬盘里

模拟dispatch_once

目标c单例dispatch_once实现更好?

Cocoa dispatch_once 每个实例