Why does Apple recommend to use dispatch_once for implementing the singleton pattern under ARC?
在arc下的singleton的共享实例访问器中使用dispatch-once的确切原因是什么?
1 2 3 4 5 6 7 8 9 10 11
| + (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;
} |
在后台异步实例化单例不是个坏主意吗?我的意思是,如果我请求共享实例并立即依赖它,但在圣诞节之前调度一次来创建我的对象会发生什么?它不会立即返回,对吗?至少,这似乎是大中央调度的全部要点。
那他们为什么要这样做?
- Note: static and global variables default to zero.
dispatch_once()是绝对同步的。并非所有的gcd方法都是异步的(例如,dispatch_sync()是同步的)。使用dispatch_once()替代了以下习惯用法:
1 2 3 4 5 6 7 8 9
| + (MyClass *)sharedInstance {
static MyClass *sharedInstance;
@synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[MyClass alloc] init];
}
}
return sharedInstance;
} |
dispatch_once()的好处在于它更快。它在语义上也更清晰,因为它还可以防止多个线程执行sharedinstance的alloc init——如果它们同时尝试的话。它不允许创建两个实例。dispatch_once()的全部思想是"做一次,而且只做一次",这正是我们正在做的。
- 为了说明这个论点,我需要注意的是,文档并不是说它是同步执行的。它只表示多个同时调用将被序列化。
- 你说它更快-快多少?我没有理由认为你没有说实话,但我想看看一个简单的基准。
- 我刚刚做了一个简单的基准测试(在iPhone5上),看起来调度速度一度比@synchronized快2倍。
- 像这样的单身汉有什么不好?考虑到我不能从多个线程访问…静态myclass*_instance;+(myclass*)instance返回!实例?_ instance=myclass.new:_instance;
- @Renedohan:如果你百分之百确定没有人从不同的线程调用过这个方法,那么它就可以工作了。但是使用dispatch_once()确实很简单(特别是因为xcode甚至会自动将其完成为完整的代码片段),这意味着您甚至不必考虑该方法是否需要线程安全。
- 它当然比@synchronized快,但不会比initialize方法中的静态变量赋值快。
- @秀英:事实上,那不是真的。首先,在+initialize中所做的任何事情都发生在类被触摸之前,即使您还没有尝试创建共享实例。一般来说,延迟初始化(仅在需要时创建一些东西)更好。第二,即使是你的表现也不真实。dispatch_once()所用的时间几乎与+initialize中if (self == [MyClass class])所用的时间相同。如果您已经有了一个+initialize,那么在那里创建共享实例会更快,但大多数类不会。
- @秀英:而且,性能胜利是毫无意义的。我昨天做了+initialize和if (self == [MyClass class])对dispatch_once()的基准测试,发现在ipad air上的arm64上,两个测试第一次都超过了2000次,第二次都超过了1600次(在没有争议的情况下,dispatch_once()在第二次调用上更快,而在+initialize完成后,调用[MyClass class]的速度更快。原因)2000纳秒简直是微不足道的。
- 在这个基准测试中发现,dispatch_once()更快:bjhomer.blogspot.com/2011/09/synchronized vs dispatchonce.ht‌&8203;ml
- @英西:比埃多克斯1〔13〕?绝对!速度惊人,毫无疑问。最近评论中的讨论将其与在+initialize中使用[MyClass class] == self进行了比较,这是没有明确答案的地方。
- @专家:当前文档说"如果从多个线程同时调用,则此函数将同步等待,直到块完成。"
- @slippd.thompson所以,从技术上讲,如果只从一个线程调用,它不会说它是同步的。苹果本可以更精确的定义。
- @Fishinear好吧,当然,那些文件不是很清楚。但除此之外,您还可以自己测试它,或者(更好的是)在github.com/apple/swift-corelibs-libdispatch/blob/master/src/‌&8203;…上阅读dispatch_once()的开源代码。你会发现它是完全同步的。因此,我采取的立场是,有相当数量的证据表明它是同步的,而没有证据表明它是异步的,这是一个安全合理的假设,可以使dispatch_once()始终是同步的,相反的论点只不过是F.U.D.支持它们。
- slippd.thompson曼弗雷迪或法拉利吗"是专家与我arguing EN,EN是异步的,最肯定的is not。我们在the argument is the documentation制作不完全知道它understandable that is,布尔人(我喜欢的作品misunderstand恩)。
因为它只能运行一次。所以,如果您尝试从不同的线程访问它两次,就不会产生问题。
MikeAsh在他护理和喂养单身汉的博客帖子中有一个完整的描述。
并非所有GCD块都是异步运行的。
- 莉莉的回答比较好,但我要离开我的,继续和麦克·阿什的帖子保持联系。