2

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

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

4

1 回答 1

4

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

如果您删除外部检查,我认为这两个版本都可以。

你可能感兴趣

其中有一个很好的答案,解释了实现和性能的差异。

于 2013-09-17T08:47:50.123 回答