iOS上的线程安全懒惰初始化 [英] Thread safe lazy initialization on iOS
问题描述
我有一个要懒惰初始化的视图控制器,一旦初始化,在可能的情况下使用相同的副本(我不使用单例,因为我想最终将其从内存中删除),我使用吸气剂这样做,我的代码如下所示:
I have a view controller that I want to lazily initialize, and once initialized, use the same copy when possible (I don't use a singleton since I do want to remove it from memory eventually), I use the getter to do so, my code look like this:
@property (retain) UIViewController *myController
...
@synthesize myController = _myController;
...
- (UIViewController *)myController
{
if (!_myController) { // Evaluation
_myController = [[MyViewController alloc] init]; // Object Creation
}
return _myController;
}
这有效,但是它不是线程安全的,并且如果在创建对象之前多个线程的求值结果为true,则将发生内存泄漏.我尝试过的一种解决方案是@synchronized代码,但是我不确定这样做的正确方法.
This works, but it's not thread safe, and if more than one thread evaluate to true before the object is created, I'll have a memory leak. One solution I've tried is to @synchronized the code, but I'm not sure the correct way to do it.
这看起来很有效,(lockForMyController是一个简单的NSString),但是这会使这段代码慢很多:
This appears to work, (lockForMyController is a simple NSString) but it makes this section of code a lot slower:
- (UIViewController *)myController
{
@synchronized(self.lockForMyController){
if (!_myController) {
_myController = [[MyViewController alloc] init];
}
}
return _myController;
}
我想知道是否还有其他方法可以实现延迟初始化,线程安全的属性?
I was wondering if there is some other way to achieve a lazy initialized, thread safe, property?
推荐答案
此解决方案有效
请注意,仅当首次在后台线程上访问myController时,此解决方案才有效.如果在主线程上调用它将死锁.
Note that this solution only works if myController is accessed on a background thread the first time. It will deadlock if called on the main thread.
您要使用gcd.关键是序列化对象的创建,因此无论启动该块的线程如何,它始终只会被创建一次.
You want to use gcd. The key is serialize the creation of the object, so that regardless of the threads starting the block, it will always only be created exactly once.
- (UIViewController *)myController
if (_myController == nil) {
dispatch_sync(dispatch_get_main_queue(), ^ { if (_myController == nil) _myController = [[MyViewController alloc] init]; });
}
return _myController;
}
在这里,即使有多个线程执行该块,该块的执行也会被序列化到主线程上,并且只能创建一个MyViewController.
Here, even if multiple threads execute the block, the execution of the block is serialized onto the main thread and only one MyViewController can ever be created.
除非对象为零,否则您不会在此处看到性能下降.
You won't see a performance hit here unless the object is nil.
由于该属性是隐式原子的,这意味着在setter中,该值将被自动释放.这应该使其适合与您的自定义获取混合,因为它将自动释放对_myController的任何值更改.
Since the property is implicitly atomic, that means that in the setter the value will be autoreleased. This should make it suitable for mingling with your custom getting, since it will autorelease any value changes to _myController.
但是,您仍然可能进入竞争状态,即您在一个线程上设置该值而在另一个线程上访问该值.每次设置该值时,您可能都想确定并执行以下操作:
However, you still may get into a race condition where you are setting the value on one thread but accessing it on another. Any time you set the value, you probably want to make sure and do something like this:
dispatch_sync(dispatch_get_main_queue(),^ {self.myController = {newValueOrNil}});
dispatch_sync(dispatch_get_main_queue(), ^ { self.myController = {newValueOrNil} });
这将确保序列化您的setter方法调用,而不必重新发明原子型setter的方法,这很难做到.
This will make sure to serialize your setter methods calls without having to reinvent the wheel for atomic setters, which is very hard to get right.
此解决方案无效
您要使用gcd.
请参阅有关单例的这篇文章.我知道您不想要单例,但这演示了如何使用该方法.您可以轻松地对其进行调整.
See this post about singletons. I know you don't want a singleton, but this demonstrates how to use the method. You can easily adapt it.
这篇关于iOS上的线程安全懒惰初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!