with ARC: how to implement a custom atomic property?

586 Views Asked by At

In ARC, I want an atomic property for multi-thread access:

@interface MyClass
@property (atomic, strong) NSString *myString;
@end

I also want a custom setter and getter for various reasons, like debugging or tracking.

Solution A, I override the setter and getter:

@implementation MyClass
@synthesize myString = _myString;
static int i;
- (void)setMyString:(NSString *)myString
{
    _myString = myString;
    NSLog(@"%d", i++);
}
- (NSString *)myString
{
    return _myString;
}
@end

Solution B, I extend the class interface to have a different synthesized property:

@interface MyClass ()
@property (atomic, strong) NSString *myPrivateString;
@end

@implementation MyClass
@synthesize myPrivateString = _myPrivateString;
static int i;
- (void)setMyString:(NSString *)myString
{
    _myPrivateString = myString;
    NSLog(@"%d", i++);
}
- (NSString *)myString
{
    return _myPrivateString;
}
@end

Is solution A atomic? Is solution B atomic? If none of them are atomic, should I use objc_setProperty and objc_getProperty, or should I use @synchronized(self), or is there a better locking mechanism to avoid locking self?

(sub-question: in case B, getting or setting _myPrivateString directly is as safe as getting or setting self.myPrivateString or not as safe?)

2

There are 2 best solutions below

0
gnasher729 On

Because you are using an instance variable without a leading underscore, you have fallen into the trap of actually accessing the instance variable instead of the accessor method. When you access the instance variable, "atomic" won't do _anything" for you. Anyone with any Objective-C experience will advice you very, very strongly not to use instance variables without the leading underscore. Just remove the @synthesize (so you built yourself a trap, fell into it, and you actually did extra work to do so).

So none of your solutions is in any way atomic.

2
trojanfoe On

Is solution A atomic?

No, it's not. As you are providing a custom version of the getter/setter methods, you'll also need to provide the locking mechanism. Using @synchronized(self) will work, however if there is high contention for this property, you might want to create a locking object for each property. That's unlikely, however.

Is solution B atomic?

Yes it is, however be sure to use self.myPrivateString = myString; to ensure you access the getter/setter methods, where the locking is implemented.

I would recommend using solution A as it's simple, which is always good.