Thread safe lazy initialization on iOS

我与影子孤独终老i 提交于 2019-12-18 12:06:14

问题


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;
}

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.

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?


回答1:


This solution works

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.

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;
}

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.

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.

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

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} });

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.

This solution does not work

You want to use gcd.

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

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.

Create singleton using GCD's dispatch_once in Objective C



来源:https://stackoverflow.com/questions/7856733/thread-safe-lazy-initialization-on-ios

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!