iOS上的线程安全延迟初始化

时间:2011-10-22 00:58:29

标签: ios thread-safety lazy-loading

我有一个视图控制器,我想懒洋洋地初始化,并且一旦初始化,尽可能使用相同的副本(我不使用单例,因为我想最终从内存中删除它),我使用getter来这样做,我的代码看起来像这样:

@property (retain) UIViewController *myController

...

@synthesize myController = _myController;


...


- (UIViewController *)myController
{
    if (!_myController) {                                 // Evaluation
        _myController = [[MyViewController alloc] init];  // Object Creation
    }
    return _myController;
}

这有效,但它不是线程安全的,如果在创建对象之前多个线程评估为true,我将发生内存泄漏。我尝试过的一个解决方案是@synchronized代码,但我不确定正确的方法。

这似乎有用,(lockForMyController是一个简单的NSString),但它使这部分代码慢了很多:

 - (UIViewController *)myController
{
    @synchronized(self.lockForMyController){
        if (!_myController) {
            _myController = [[MyViewController alloc] init];
        }
    }
    return _myController;
}

我想知道是否还有其他方法可以实现延迟初始化,线程安全,属性?

1 个答案:

答案 0 :(得分:9)

此解决方案有效

请注意,此解决方案仅在第一次在后台线程上访问myController时才有效。如果在主线程上调用它将会死锁。

你想使用gcd。关键是序列化对象的创建,这样无论启动块的线程如何,它总是只创建一次。

- (UIViewController *)myController
    if (_myController == nil) {
        dispatch_sync(dispatch_get_main_queue(), ^ { if (_myController == nil) _myController = [[MyViewController alloc] init]; });
    }
    return _myController;
}

这里,即使多个线程执行该块,块的执行也被序列化到主线程上,并且只能创建一个MyViewController。

除非对象为零,否则您不会在此处看到性能损失。

由于属性是隐式原子的,这意味着在setter中值将被自动释放。这应该使它适合与你的自定义获取混合,因为它将自动释放任何值更改为_myController。

http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW2

但是,您仍然可能陷入竞争状态,您在一个线程上设置值但在另一个线程上访问它。无论何时设置该值,您可能需要确保并执行以下操作:

dispatch_sync(dispatch_get_main_queue(),^ {self.myController = {newValueOrNil}});

这将确保序列化您的setter方法调用,而不必为原子设置器重新发明轮子,这很难做到。

此解决方案不起作用

你想使用gcd。

http://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_once

查看关于单身人士的这篇文章。我知道你不需要单身,但这演示了如何使用该方法。你可以很容易地适应它。

Create singleton using GCD's dispatch_once in Objective C