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

iOS上的线程安全延迟初始化,ios,thread-safety,lazy-loading,Ios,Thread Safety,Lazy Loading,我有一个视图控制器,我想延迟初始化,一旦初始化,尽可能使用相同的副本(我不使用单例,因为我确实希望最终将其从内存中删除),我使用getter来完成,我的代码如下所示: @property (retain) UIViewController *myController ... @synthesize myController = _myController; ... - (UIViewController *)myController { if (!_myController)

我有一个视图控制器,我想延迟初始化,一旦初始化,尽可能使用相同的副本(我不使用单例,因为我确实希望最终将其从内存中删除),我使用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;
}

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

此解决方案有效

请注意,此解决方案仅在首次在后台线程上访问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

但是,您仍然可能会进入一种竞争条件,即您在一个线程上设置值,但在另一个线程上访问它。无论何时设置该值,您都可能希望确保并执行以下操作:

@property (retain) UIViewController *myController

...

@synthesize myController = _myController;


...


- (UIViewController *)myController
{
    if (!_myController) {                                 // Evaluation
        _myController = [[MyViewController alloc] init];  // Object Creation
    }
    return _myController;
}
dispatch_sync(dispatch_get_main_queue(),^{self.myController={newValueOrNil})

这将确保序列化setter方法调用,而不必为原子setter重新发明轮子,这是非常困难的

此解决方案不起作用

你想用gcd

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


我很难适应它。我可以让它像单例一样工作,但是当我使用
[\u myController release]时,我会遇到问题_myController=nil
然后我尝试再次创建它,因为我无法重置
static dispatch\u once\u t once
变量。嗯,让我用一个建议更新我的问题,这个建议应该有效。有人能澄清我对这个解决方案的困惑吗?如果从主线程调用,这个块不会永远被阻塞吗?《并发编程指南》中:“重要信息-决不能从计划传递给函数的同一队列中执行的任务调用dispatch_sync或dispatch_sync函数。这对于串行队列尤其重要,串行队列保证会死锁,但对于并发队列也应避免。”您是正确的,如果您曾经从主线程访问它,这将不起作用。海报显然总是从后台线程使用它,所以它对他们有效。@Evagrim:因为“主线程”不是“当前队列”。从主线程调用dispatch_sync是GCD的主要用例。但是,如果使用dispatch_sync分派任务,并且该任务试图将任务分派到同一队列,则bam死锁。