我想知道以下两个延迟初始化的解决方案是否正确。
我有一个类AppContext
,它应该保存对应该只存在一次的其他类的引用(避免使这些类中的每一个都成为单例)。假设其中一个类称为ReferencedClass
。话虽这么说,我想以线程安全的方式使用默认值来初始化引用。
之前已经讨论过了,我已经阅读了很多关于它的内容,但我仍然不确定。除了个人偏好之外,我想知道的是:这两种解决方案是否是实现我所期望的行为的正确方法?
解决方案1:最初我想实现它:
// Getter with lazy initialized default value
- (ReferencedClass *)referencedClass {
// Check if nil. If yes, wait for lock and check again after locking.
if (_referencedClass == nil) {
@synchronized(self) {
if (_referencedClass == nil) {
// Prevent _referencedClass pointing to partially initialized objects
ReferencedClass *temp = [[ReferencedClass alloc] init];
_referencedClass = temp;
}
}
}
return _referencedClass;
}
// Setter
- (void)setReferencedClass:(ReferencedClass *)referencedClass {
@synchronized(self) {
_referencedClass = referencedClass;
}
}
解决方案2:然后我决定改用GCD,所以我写了这个:
// Getter with lazy initialized default value
- (ReferencedClass *)referencedClass {
// Check if nil. If yes, wait for "lock" and check again after "locking".
if (_referencedClass == nil) {
dispatch_sync(syncDispatchQueue, ^{
if (_referencedClass == nil) {
// Prevent _referencedClass pointing to partially initialized objects
ReferencedClass *temp = [[ReferencedClass alloc] init];
_referencedClass = temp;
}
});
}
return _referencedClass;
}
// Setter
- (void)setReferencedClass:(ReferencedClass *)referencedClass {
dispatch_sync(syncDispatchQueue, ^{
_referencedClass = referencedClass;
});
}
当然,在某个地方(例如在init
- 方法中)我已经使用以下内容初始化syncDispatchQueue
:
syncDispatchQueue = dispatch_queue_create("com.stackoverflow.lazy", NULL);
这是正确的,线程安全且无死锁的代码吗?我可以使用双重检查锁定和temp
- 变量吗?如果这种双重检查锁定不安全,如果我删除外部检查,我的代码在两种情况下都是安全的吗?我想是的,对吧?
非常感谢!
[旁注: 我知道dispatch_once,并且有人说(与Apple文档相反)它也可以与实例变量一起使用。现在我想使用这两个选项中的一个。如果可能的话。]
答案 0 :(得分:3)
据我了解,您的“双重检查锁定”机制不线程安全,
因为分配_referencedClass = ...
不是原子的。因此,一个线程可能会在外部if (_referencedClass == nil)
检查中读取部分初始化的变量。
如果删除外部检查,两个版本对我来说都没问题。
您可能对
感兴趣有一个很好的答案,解释了实施和绩效的差异。