Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-c properties for primitive types

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;
like image 658
SundayMonday Avatar asked Oct 17 '12 15:10

SundayMonday


People also ask

What is primitive data type in Objective-C?

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.

What are the characteristics of primitive data types?

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)

What are Objective-C properties?

The Objective-C declared properties feature provides a simple way to declare and implement an object's accessor methods.


2 Answers

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.

like image 109
Rhubarb Avatar answered Oct 24 '22 07:10

Rhubarb


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

  • Pointers are stored in a single memory position but they require several statements for memory management.

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.

like image 29
Jano Avatar answered Oct 24 '22 05:10

Jano