I would like to know if both of the following solutions for lazy initialization are correct.
I have a class AppContext
that is supposed to hold references to other class that should only exist once (Avoiding making every single one of these classes a singleton). Let's say one of these other classes is called ReferencedClass
. That being said, I would like to lazy-initialize the references with defaults, in a thread-safe way.
It has been discussed before, and I have read a lot about it, but I am still unsure. Personal preferences aside, what I would like know is: Are these two solutions a correct way to implemented my desired behavior?
Solution 1: Originally I wanted to implement it like this:
// Getter with lazy initialized default value
- (ReferencedClass *)referencedClass {
// Check if nil. If yes, wait for lock and check again after locking.
if (_referencedClass == nil) {
@synchronized(self) {
if (_referencedClass == nil) {
// Prevent _referencedClass pointing to partially initialized objects
ReferencedClass *temp = [[ReferencedClass alloc] init];
_referencedClass = temp;
}
}
}
return _referencedClass;
}
// Setter
- (void)setReferencedClass:(ReferencedClass *)referencedClass {
@synchronized(self) {
_referencedClass = referencedClass;
}
}
Solution 2: Then I decided to go with GCD instead, so I wrote this:
// Getter with lazy initialized default value
- (ReferencedClass *)referencedClass {
// Check if nil. If yes, wait for "lock" and check again after "locking".
if (_referencedClass == nil) {
dispatch_sync(syncDispatchQueue, ^{
if (_referencedClass == nil) {
// Prevent _referencedClass pointing to partially initialized objects
ReferencedClass *temp = [[ReferencedClass alloc] init];
_referencedClass = temp;
}
});
}
return _referencedClass;
}
// Setter
- (void)setReferencedClass:(ReferencedClass *)referencedClass {
dispatch_sync(syncDispatchQueue, ^{
_referencedClass = referencedClass;
});
}
Of course, somewhere (for example in the init
-Method) I have initialized the syncDispatchQueue
with something like:
syncDispatchQueue = dispatch_queue_create("com.stackoverflow.lazy", NULL);
Is this correct, thread-safe and deadlock-free code? Can I use the double-checked-locking together with the temp
-variable? If this double-checked-locking is not safe, would my code in both cases be safe if I removed the outer checks? I guess so, right?
Thanks very much in advance!
[Side note: I am aware of dispatch_once and that some people say that (in contrary to the Apple documentation) it can also be used with instance variables. For now I would like to use one of these two options though. If possible. ]