Categories
atomic ios nonatomic objective-c properties

What’s the difference between the atomic and nonatomic attributes?

1921

What do atomic and nonatomic mean in property declarations?

@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;

What is the operational difference between these three?

1

1800

The last two are identical; “atomic” is the default behavior (note that it is not actually a keyword; it is specified only by the absence of nonatomicatomic was added as a keyword in recent versions of llvm/clang).

Assuming that you are @synthesizing the method implementations, atomic vs. non-atomic changes the generated code. If you are writing your own setter/getters, atomic/nonatomic/retain/assign/copy are merely advisory. (Note: @synthesize is now the default behavior in recent versions of LLVM. There is also no need to declare instance variables; they will be synthesized automatically, too, and will have an _ prepended to their name to prevent accidental direct access).

With “atomic”, the synthesized setter/getter will ensure that a whole value is always returned from the getter or set by the setter, regardless of setter activity on any other thread. That is, if thread A is in the middle of the getter while thread B calls the setter, an actual viable value — an autoreleased object, most likely — will be returned to the caller in A.

In nonatomic, no such guarantees are made. Thus, nonatomic is considerably faster than “atomic”.

What “atomic” does not do is make any guarantees about thread safety. If thread A is calling the getter simultaneously with thread B and C calling the setter with different values, thread A may get any one of the three values returned — the one prior to any setters being called or either of the values passed into the setters in B and C. Likewise, the object may end up with the value from B or C, no way to tell.

Ensuring data integrity — one of the primary challenges of multi-threaded programming — is achieved by other means.

Adding to this:

atomicity of a single property also cannot guarantee thread safety when multiple dependent properties are in play.

Consider:

 @property(atomic, copy) NSString *firstName;
 @property(atomic, copy) NSString *lastName;
 @property(readonly, atomic, copy) NSString *fullName;

In this case, thread A could be renaming the object by calling setFirstName: and then calling setLastName:. In the meantime, thread B may call fullName in between thread A’s two calls and will receive the new first name coupled with the old last name.

To address this, you need a transactional model. I.e. some other kind of synchronization and/or exclusion that allows one to exclude access to fullName while the dependent properties are being updated.

21

  • 22

    Given that any thread-safe code will be doing its own locking etc, when would you want to use atomic property accessors? I’m having trouble thinking of a good example.

    May 24, 2011 at 20:00

  • 8

    @bbum Makes sense. I like your comment to another answer that thread-safety is more a model-level concern. From an IBM thread safety definition: ibm.co/yTEbjY “If a class is correctly implemented, which is another way of saying that it conforms to its specification, no sequence of operations (reads or writes of public fields and calls to public methods) on objects of that class should be able to put the object into an invalid state, observe the object to be in an invalid state, or violate any of the class’s invariants, preconditions, or postconditions.”

    – Ben Flynn

    Jan 26, 2012 at 19:22

  • 6

    Here’s an example similar to @StevenKramer ‘s: I have a @property NSArray* astronomicalEvents; that lists data I want to display in the UI. When the application launches the pointer points to an empty array, then the app pulls data from the web. When the web request completes (in a different thread) the app builds a new array then atomically sets the property to a new pointer value. It’s thread safe and I didn’t have to write any locking code, unless I’m missing something. Seems pretty useful to me.

    – bugloaf

    Feb 15, 2013 at 15:52


  • 12

    @HotLicks Another fun one; on certain architectures (Can’t remember which one), 64 bit values passed as an argument might be passed half in a register and half on the stack. atomic prevents cross-thread half-value reads. (That was a fun bug to track down.)

    – bbum

    Nov 23, 2013 at 23:19

  • 8

    @congliu Thread A returns an object without retain/autorelease dance. Thread B releases object. Thread A goes boom. atomic ensures that thread A has a strong reference (a +1 retain count) for the return value.

    – bbum

    Dec 6, 2013 at 7:13

364

This is explained in Apple’s documentation, but below are some examples of what is actually happening.

Note that there is no “atomic” keyword, if you do not specify “nonatomic”, then the property is atomic, but specifying “atomic” explicitly will result in an error.

If you do not specify “nonatomic”, then the property is atomic, but you can still specify “atomic” explicitly in recent versions if you want to.

//@property(nonatomic, retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    return userName;
}

- (void) setUserName:(UITextField *)userName_ {
    [userName_ retain];
    [userName release];
    userName = userName_;
}

Now, the atomic variant is a bit more complicated:

//@property(retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    UITextField *retval = nil;
    @synchronized(self) {
        retval = [[userName retain] autorelease];
    }
    return retval;
}

- (void) setUserName:(UITextField *)userName_ {
    @synchronized(self) {
      [userName_ retain];
      [userName release];
      userName = userName_;
    }
}

Basically, the atomic version has to take a lock in order to guarantee thread safety, and also is bumping the ref count on the object (and the autorelease count to balance it) so that the object is guaranteed to exist for the caller, otherwise there is a potential race condition if another thread is setting the value, causing the ref count to drop to 0.

There are actually a large number of different variants of how these things work depending on whether the properties are scalar values or objects, and how retain, copy, readonly, nonatomic, etc interact. In general the property synthesizers just know how to do the “right thing” for all combinations.

6

  • 8

    @Louis Gerbarg: I believe your version of the (nonatomic, retain) setter will not work properly if you try to assign the same object (that is: userName == userName_)

    – Florin

    Aug 12, 2010 at 9:29


  • 5

    Your code is slightly misleading; there is no guarantee on what atomic getters/setters are synchronized. Critically,@property (assign) id delegate; is not synchronized on anything (iOS SDK GCC 4.2 ARM -Os), which means there’s a race between [self.delegate delegateMethod:self]; and foo.delegate = nil; self.foo = nil; [super dealloc];. See stackoverflow.com/questions/917884/…

    – tc.

    Dec 1, 2010 at 18:20

  • @fyolnish I’m not sure what _val/val are, but no, not really. The getter for an atomic copy/retain property needs to ensure that it does not return an object whose refcount becomes zero due the setter being called in another thread, which essentially means it needs to read the ivar, retain it while ensuring that the setter hasn’t overwritten-and-released it, and then autorelease it to balance the retain. That essentially means both the getter and setter have to use a lock (if the memory layout was fixed it should be doable with CAS2 instructions; alas -retain is a method call).

    – tc.

    Dec 10, 2013 at 11:09

  • @tc It’s been quite a while but what I meant to write was probably this: gist.github.com/fjolnir/5d96b3272c6255f6baae But yes it is possible for the old value to be read by a reader before setFoo: returns, and released before the reader returns it. But maybe if the setter used -autorelease instead of -release, that would fix that.

    – Fjölnir

    Dec 11, 2013 at 5:47


  • @fyolnish Unfortunately, no: That autoreleases on the thread of the setter, while it needs to be autoreleased on the thread of the getter. It also looks like there’s a (slim) chance of running out of stack because you’re using recursion.

    – tc.

    Dec 11, 2013 at 13:18

174

Atomic

  • is the default behavior
  • will ensure the present process is completed by the CPU, before another process accesses the variable
  • is not fast, as it ensures the process is completed entirely

Non-Atomic

  • is NOT the default behavior
  • faster (for synthesized code, that is, for variables created using @property and @synthesize)
  • not thread-safe
  • may result in unexpected behavior, when two different process access the same variable at the same time

0