Question

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. ]

Was it helpful?

Solution

As far as I understand it, your "double-checked locking" mechanism is not thread-safe, because the assigment _referencedClass = ... is not atomic. So one thread might read a partially initialized variable in the outer if (_referencedClass == nil) check.

If you remove the outer checks, both versions look OK to me.

You may be interested in

which has a great answer explaining the differences in implementation and performance.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top