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

标签 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-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/

相关文章:

iphone - MapKit 注释和用户位置

iphone - for 循环 - 忽略对象类型?

c - 多线程 Linux 程序没有给出预期的输出

AndroidObservable 从不在主线程上观察

ios - UITableView 重新加载不使用 DispatchQueue.main.async 更新 TableView

c++ - 从 Objective-C 类调用 C++ 构造函数

c++ - 使用类别方法在 NSDecimalNumber 上重载算术运算符

python - 关于为什么 Lock 机制没有在下面的 python 脚本中实现的任何建议?

parallel-processing - Swift 是否具有通过 Grand Central Dispatch 的 dispatch_async 进行并行编程的结构?

ios - 从不正确的线程访问的 Realm - Swift 3