srus2017
srus2017

Reputation: 404

Thread Safety - lazily initialized property getter - Objective C

Suppose if I want to implement both getter and setter I will do like this -

@interface Person () {
 dispatch_queue_t _myConcurrentQueue;
}

@property (nonatomic, copy) NSString *personName;

@end

@implementation Person


- (instancetype)init
{
    if (self = [super init])
    {
        _myConcurrentQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
    }
    return self;
}

@synthesize personName = _personName;

- (NSString *)personName {
    __block NSString *tmp;
    dispatch_sync(_myConcurrentQueue, ^{
        tmp = _personName;
    });
    return tmp;
}

- (void)setpersonName:(NSString *)personName {
    NSString* tmp = [personName copy];
    dispatch_barrier_async(_myConcurrentQueue, ^{
        _personName = tmp;
    });
}

@end

But if I want to initialize my property lazily, then how can I make it thread safe? Example -

- (NSString *)personName {
        If (!_personName) {
           _personName = "Some Name"
         }

    return _personName;
}

What should I use serial queue with dispatch_async or concurrent queue with dispatch_async barrier and why?

Upvotes: 0

Views: 136

Answers (1)

Ken Thomases
Ken Thomases

Reputation: 90571

Well, in your trivial case using a string literal (although you neglected the @), there's little chance to go wrong.

However, for the general case where you initialize the instance variable in a more dynamic fashion with possible side effects, no, it's not thread-safe. Two threads can both be in the getter simultaneously. They can each submit a task to the queue. Since the queue is concurrent and neither task was submitted as a barrier, both tasks can run simultaneously. They might both test !_personName and both determine that they need to initialize it. They will do redundant work and, if it has side effects, that could screw things up.

It's also possible that the compiler and/or CPU can re-order how things are done. One thread may think that _personName is non-nil and return the pointer, even though the other thread is still working on initializing the object. That is, the assignment to _personName may happen before all of the effects of the expression on the right-hand side of the assignment have completed and are visible to the other thread.

Upvotes: 2

Related Questions