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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With