我想知道以下两种延迟初始化的解决方案是否正确。
我有一个类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
-Method 中)我已经用类似以下内容初始化了 syncDispatchQueue
:
syncDispatchQueue = dispatch_queue_create("com.stackoverflow.lazy", NULL);
这是正确的、线程安全的、无死锁的代码吗?我可以将双重检查锁定与 temp
变量一起使用吗?如果这种双重检查锁定不安全,那么如果我删除外部检查,我在这两种情况下的代码都会安全吗?我想是的,对吗?
提前非常感谢!
[旁注: 我知道dispatch_once,并且有人说(与Apple文档相反)它也可以与实例变量一起使用。但现在我想使用这两个选项之一。如果可能的话。]
最佳答案
据我了解,您的“双重检查锁定”机制不是线程安全的,
因为赋值 _referencedClass = ...
不是原子的。因此,一个线程可能会在外部 if (_referencedClass == nil)
检查中读取部分初始化的变量。
如果删除外部检查,两个版本对我来说都看起来不错。
您可能感兴趣
其中有一个很好的答案,解释了实现和性能的差异。
关于objective-c - getter 中的线程安全延迟初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18844389/