getter中的线程安全延迟初始化

时间:2013-09-17 08:00:38

标签: objective-c multithreading grand-central-dispatch getter

我想知道以下两个延迟初始化的解决方案是否正确。

我有一个类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文档相反)它也可以与实例变量一起使用。现在我想使用这两个选项中的一个。如果可能的话。]

1 个答案:

答案 0 :(得分:3)

据我了解,您的“双重检查锁定”机制线程安全, 因为分配_referencedClass = ...不是原子的。因此,一个线程可能会在外部if (_referencedClass == nil)检查中读取部分初始化的变量。

如果删除外部检查,两个版本对我来说都没问题。

您可能对

感兴趣

有一个很好的答案,解释了实施和绩效的差异。