In Objective-C Does it ever make sense to specify a property for a primitive type as nonatomic
?
I'm wondering about the difference between these two properties:
@property (nonatomic) BOOL myBool;
@property BOOL myBool;
There are two primitive floating point types available in Objective-C: The float type is a 32-bit single precision float point value that uses a single sign bit, an 8-bit mantissa and 23 bits of exponent when storing numbers.
NET, the primitive data types consist of 4 integral types, 2 floating-point types, a 16-byte decimal type, a boolean type, a date/time type, a Unicode character type, and a Unicode string type. In general, data types that can be built into sophisticated programming languages include: Characters and strings (see below)
The Objective-C declared properties feature provides a simple way to declare and implement an object's accessor methods.
The answer is technically YES they're different, but practically NO they're not, unless you code your own accessors.
Let me explain. For an object pointer property, say @property NSObject *foo
there is a clear and important difference in the code that gets generated if you use synthesize the accessors. This is described in the Apple documentation where it points out that the synthesized accessors will lock the object if the property is atomic (if you don't specify nonatomic, it becomes atomic by default)
So for object properties In a very crude nutshell: nonatomic is faster but not threadsafe, atomic (you can't specify it, but its the default) is threadsafe but potentially slower.
(Note: if you're accustomed to Java, you can think of using nonatomic
as like not specifying synchronized
, and not specifying nonatomic
as like specifying synchronized
. In other words atomic = synchronized)
But a BOOL is a primitive - in fact a C signed char, so access should be atomic without locking the object as mentioned in Jano's answer. So when you're synthesizing the accessor, there are two possibilities: 1: the compiler is smart and sees that the property is primitive and avoids locking it, and 2: the compiler always locks the object for atomic properties
As far as I can see this is not documented anywhere, so I tried it by using the Generate->Assembly option in XCode and comparing it. The answer was not completely conclusive, but close enough to say that I'm almost certain the answer is #1, the compiler is smart. I say this, because the assembly code generated for an atomic object property is considerably different (more of it) than for a non-atomic object property: this is all the code for locking the object. For a BOOL property on the other hand, there was only one line different - a single "mov" which doesn't look like it could possibly make a difference. Still I wonder. Interestingly, another difference is that the atomic version of BOOL has some extra commented-outlines for debugging - so the compiler is clearly treating it differently.
Nonetheless the similarity is such that I would say they're the same for practical purposes.
But they're still technically different and may be substantively different in some other library you're reading (that you didn't code yourself) if you can't see the implementation, and here's why: atomic properties have a contract. The contract says: "If you access my value on multiple threads, I promise that each setting or getting operation will complete before any other begins".
But, you say, BOOL is still naturally atomic, so isn't this contract implicit?
No. A BOOL variable is naturally atomic, but we're talking about a property. A property might not be synthesized and might not even have a single variable to back it up. This is actually pretty common. Consider:
@property (getter=isEmptyThingo) BOOL emptyThingo;
...
- (BOOL)isEmptyThingo
{
Thingo *thingo = [self deriveTheThingo];
if ([thingo length] == 0) {
return YES;
}
return NO;
}
who knows what's going on in deriveTheThingo
!?
Okay, this is a bit contrived, but the point is that isEmptyThingo - our getter doesn't look very atomic, does it? What happens if one thread is deriving the thingo and another thread comes calling to find if its empty.
Long story short: the property is not atomic. So we should declare it so.
Hence m original answer qualified: if you're writing this property yourself and using @synthesize, then they're probably the same, but you should generally not treat them the same.
As a rule of thumb, if you don't need multithreaded support - which you generally don't if you're working in UI code like UIViewControllers, then just declare it all nonatomic.
In a x-bit architecture (eg: 32bit, 64bit, etc.) any value which is x or less bits will always be read or written atomically. This is a property of any sane hardware implementation.
The default atomic property means that a property value is always set or get in whole, disregarding what other threads are doing. This is only a concern for properties that exceed the number of bits of the architecture. Nonatomic is completely ignored by the compiler on any other type.
Example:
@property struct { int d; } flag;
@property (atomic) struct { float x; float y; } point;
@property (atomic,copy) NSString *s;
struct { int d; }
is already atomic so the accesors don't need mutual exclusion.
struct { float x, float y}
could be in an inconsistent state if it wasn't atomic. Example: two threads setting {1,2}
and {3,4}
could overlap the writing, and the struct could end up with a value from each set: {1,4}
.
Atomic properties contribute to thread safety avoiding race conditions that lead to inconsistent values or memory management mishaps. This alone doesn't guarantee thread safety because it doesn't deal with other problems like deadlock, starvation, visibility, etc.
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