在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直到圣诞节才创建对象,会发生什么?它不会立即返回,对吧?至少这似乎是中央调度的全部意义所在。

他们为什么要这么做呢?


当前回答

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()的整个思想是“执行一次且仅执行一次”,这正是我们正在做的事情。

其他回答

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()的整个思想是“执行一次且仅执行一次”,这正是我们正在做的事情。

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

Mike Ash在他的“照顾和喂养独生子女”博客中有详细的描述。

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