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

513,849

Solution 1

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 nonatomic -- atomic 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.

Solution 2

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.

Solution 3

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

Solution 4

The best way to understand the difference is using the following example.

Suppose there is an atomic string property called "name", and if you call [self setName:@"A"] from thread A, call [self setName:@"B"] from thread B, and call [self name] from thread C, then all operations on different threads will be performed serially which means if one thread is executing a setter or getter, then other threads will wait.

This makes property "name" read/write safe, but if another thread, D, calls [name release] simultaneously then this operation might produce a crash because there is no setter/getter call involved here. Which means an object is read/write safe (ATOMIC), but not thread-safe as another threads can simultaneously send any type of messages to the object. The developer should ensure thread-safety for such objects.

If the property "name" was nonatomic, then all threads in above example - A,B, C and D will execute simultaneously producing any unpredictable result. In case of atomic, either one of A, B or C will execute first, but D can still execute in parallel.

Solution 5

The syntax and semantics are already well-defined by other excellent answers to this question. Because execution and performance are not detailed well, I will add my answer.

What is the functional difference between these 3?

I'd always considered atomic as a default quite curious. At the abstraction level we work at, using atomic properties for a class as a vehicle to achieve 100% thread-safety is a corner case. For truly correct multithreaded programs, intervention by the programmer is almost certainly a requirement. Meanwhile, performance characteristics and execution have not yet been detailed in depth. Having written some heavily multithreaded programs over the years, I had been declaring my properties as nonatomic the entire time because atomic was not sensible for any purpose. During discussion of the details of atomic and nonatomic properties this question, I did some profiling encountered some curious results.

Execution

Ok. The first thing I would like to clear up is that the locking implementation is implementation-defined and abstracted. Louis uses @synchronized(self) in his example -- I have seen this as a common source of confusion. The implementation does not actually use @synchronized(self); it uses object level spin locks. Louis's illustration is good for a high-level illustration using constructs we are all familiar with, but it's important to know it does not use @synchronized(self).

Another difference is that atomic properties will retain/release cycle your objects within the getter.

Performance

Here's the interesting part: Performance using atomic property accesses in uncontested (e.g. single-threaded) cases can be really very fast in some cases. In less than ideal cases, use of atomic accesses can cost more than 20 times the overhead of nonatomic. While the Contested case using 7 threads was 44 times slower for the three-byte struct (2.2 GHz Core i7 Quad Core, x86_64). The three-byte struct is an example of a very slow property.

Interesting side note: User-defined accessors of the three-byte struct were 52 times faster than the synthesized atomic accessors; or 84% the speed of synthesized nonatomic accessors.

Objects in contested cases can also exceed 50 times.

Due to the number of optimizations and variations in implementations, it's quite difficult to measure real-world impacts in these contexts. You might often hear something like "Trust it, unless you profile and find it is a problem". Due to the abstraction level, it's actually quite difficult to measure actual impact. Gleaning actual costs from profiles can be very time consuming, and due to abstractions, quite inaccurate. As well, ARC vs MRC can make a big difference.

So let's step back, not focussing on the implementation of property accesses, we'll include the usual suspects like objc_msgSend, and examine some real-world high-level results for many calls to a NSString getter in uncontested cases (values in seconds):

  • MRC | nonatomic | manually implemented getters: 2
  • MRC | nonatomic | synthesized getter: 7
  • MRC | atomic | synthesized getter: 47
  • ARC | nonatomic | synthesized getter: 38 (note: ARC's adding ref count cycling here)
  • ARC | atomic | synthesized getter: 47

As you have probably guessed, reference count activity/cycling is a significant contributor with atomics and under ARC. You would also see greater differences in contested cases.

Although I pay close attention to performance, I still say Semantics First!. Meanwhile, performance is a low priority for many projects. However, knowing execution details and costs of technologies you use certainly doesn't hurt. You should use the right technology for your needs, purposes, and abilities. Hopefully this will save you a few hours of comparisons, and help you make a better informed decision when designing your programs.

Share:
513,849
Alex Wayne
Author by

Alex Wayne

Passionate Software Developer.

Updated on July 08, 2022

Comments

  • Alex Wayne
    Alex Wayne almost 2 years

    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?

  • KeremV
    KeremV about 15 years
    Two reasons. First off, for synthesized code it generates faster (but not threadsafe code). Second, if you are writing customer accessors that are not atomic it lets you annotate for any future user that the code is not atomic when they are reading its interface, without making them implementation.
  • Jakob Dam Jensen
    Jakob Dam Jensen about 15 years
    That comment doesn't make a lot of sense. Can you clarify? If you look at examples on the Apple site then the atomic keyword synchronizes on the object while updating its properties.
  • Florin
    Florin over 13 years
    @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_)
  • tc.
    tc. over 13 years
    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/…
  • Daniel Dickison
    Daniel Dickison almost 13 years
    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.
  • bbum
    bbum almost 13 years
    If you have an accessor that'll be reading a structure (or an object) from multiple threads and your code is hardened against "is this really the current state", it is useful. I.e. if you are reading, say, a struct that has a few entries summating current status, atomic can enable safe reading.
  • BBog
    BBog over 12 years
    Nevermind, I managed to find the answer in a comment here: stackoverflow.com/a/5047416/855738 , made by bbum: "Specifically, since the locking mechanism to implement atomic is not exposed, there is no way you can manually implement just the setter or just the getter and ensure atomicity with an @synthesized atomic setter/getter"
  • Ben Flynn
    Ben Flynn over 12 years
    @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."
  • SDEZero
    SDEZero over 11 years
    MRC | atomic | synthesized getter: 47 ARC | atomic | synthesized getter: 47 What makes them the same? Should't ARC have more overhead?
  • bugloaf
    bugloaf about 11 years
    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.
  • bbum
    bbum about 11 years
    @bugloaf Yes -- you are correct (good point +1). However, most apps would typically use a completion block or handler to handle the "data is available" which would do significantly more work than just setting the property (tear down "loading", update other bits of state, etc). All the UI update has to be done on main thread anyway. So, why incur the overhead of atomic for all the accesses when the typical code already serializes on the main queue? Another case where atomic could be used, but real world patterns obviate the need.
  • Chance
    Chance about 11 years
    atomic has now become a keyword in the recent versions of clang.
  • Matthijn
    Matthijn about 11 years
    "There is such keyword", That the keyword is not required by default and even is the default value does not mean the keyword does not exist.
  • Anoop Vaidya
    Anoop Vaidya about 11 years
  • ma11hew28
    ma11hew28 almost 11 years
    Why don't they make the default nonatomic since it's used more often? Then, you could just write: @property UIWindow *window instead of @property (nonatomic) UIWindow *window for example.
  • Kunal Balani
    Kunal Balani over 10 years
    So if atomic properties are bad y are they default. To increase the boilerplate code ?
  • justin
    justin over 10 years
    @LearnCocos2D i just tested on 10.8.5 on the same machine, targeting 10.8, for the single threaded uncontested case with an NSString which is not immortal: -ARC atomic (BASELINE): 100% -ARC nonatomic, synthesised: 94% -ARC nonatomic, user defined: 86% -MRC nonatomic, user defined: 5% -MRC nonatomic, synthesised: 19% -MRC atomic: 102% -- the results are a little different today. I wasn't doing any @synchronized comparisons. @synchronized is semantically different, and I don't consider it a good tool if you have nontrivial concurrent programs. if you need speed, avoid @synchronized.
  • LearnCocos2D
    LearnCocos2D over 10 years
    do you have this test online somewhere? I keep adding mine here: github.com/LearnCocos2D/LearnCocos2D/tree/master/…
  • Hot Licks
    Hot Licks over 10 years
    @bugloaf - I don't understand how atomic serves any purpose there. If the property is only mutated in one thread, another thread examining the property pointer will find either the old pointer or the new one. I could see atomic being important if it were possible to have a partially mutated pointer, but I don't believe that's possible on any of the usual Objective-C platforms.
  • bbum
    bbum over 10 years
    @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.)
  • George
    George over 10 years
    To @bbum: What do you mean by "viable" in this comment: "@HotLicks With objects atomic ensures that the returned pointer will be viable in calling thread regardless of what other threads do. Basically, it does return [[foo retain] autorelease]; while guaranteeing that foo is viable"
  • bbum
    bbum over 10 years
    @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.
  • George
    George over 10 years
    To @bbum: Then why we use so many nonatomic property? If you mean nonatomic property may not retain/autorelease the object before return. And where is the return [[foo retain] autorelease]; code for an atomic property object? Is it the implementation of the generated getter method?
  • tc.
    tc. over 10 years
    @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).
  • Fjölnir
    Fjölnir over 10 years
    @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.
  • tc.
    tc. over 10 years
    @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.
  • Fattie
    Fattie almost 10 years
    Everything you say here is correct, but the last sentence is essentially "wrong", Dura, for today's programming. It's really inconceivable you would bother to try to "improve performance" this way. (I mean, before you got within lightyears of that, you would be "not using ARC", "not using NSString because it is slow!" and so on.) To make an extreme example, it would be like saying "team, don't put any comments in the code, as it slows us down." There is no realistic development pipeline where you would want the (nonexistent) theoretical performance gains at the sake of unreliability.
  • Durai Amuthan.H
    Durai Amuthan.H almost 10 years
  • bbum
    bbum almost 10 years
    @JoeBlow Massive overhead in context; compare atomic vs. nonatomic calls to a getter method. One is a handful of instructions more than a C function call, the other involves locking.
  • Fattie
    Fattie almost 10 years
    hey @bbum -- thanks for that report; well, I'll keep an eagle-scout eye out. I might ask an SO question about this - can anyone bring to the fore specific examples of this. CHEERS
  • 2cupsOfTech
    2cupsOfTech about 9 years
    @bbum it seem it sort of does I'm going to read further from WWDC & get it right: asciiwwdc.com/2010/sessions/138
  • sethfri
    sethfri almost 9 years
    This is incorrect. The keyword does exist. This answer is misleading, and I would encourage taking it down.
  • kevlar
    kevlar over 8 years
    Coudl we fix this answer to address @Florin's comment? I noticed the same thing and came here to comment about it.
  • bbum
    bbum about 8 years
    @GrigoriJlavyan Copy/pasting a bit of weblog content that was written in 2010 is not an answer (and, btw, the contents you copied and pasted aren't quite correct anyway).
  • Bradley Thomas
    Bradley Thomas about 8 years
    I always find it interesting that people will debate whether to making something incredibly fast, or 50 times faster but with no humanly noticeable difference between the two. It's like having a retina display and another display at 50 times the resolution. Why waste the resources to get that level of performance if it makes no difference to anyone? Especially when robust code can save days of debugging...
  • mfaani
    mfaani about 8 years
    Another Important difference is if a thread has written the first two bytes of an atomic four byte number, and a read of that number is requested on another thread, that read has to wait until all four bytes have been written. Conversely, if a thread has written the first two bytes of a non-atomic four byte number, and a read of that number is requested on another thread at that moment, it will read the first two new data bytes, but will get old data from a previous write operation in the other two bytes. stackoverflow.com/a/21098554/5175709
  • mfaani
    mfaani about 8 years
    I wanted to edit your post to this, but I decided to comment here instead: "that a whole (if you have wrote half of the value, it won't allow reading on that ‘exact halfway-wrote thread’, still you may be able to read from another whole value set by another thread)"...
  • BangOperator
    BangOperator almost 8 years
    How can assign and strong/retain both be default ?
  • peak
    peak over 7 years
    I don't know how the last paragraph comes, but it's simple wrong, there is no such thing like "private copy".
  • Alejandro Iván
    Alejandro Iván about 7 years
    As many many many maaaaany others have said, atomic is NOT thread-safe! It's more resistant to thread problems, but not thread-safe. It just ensures you will get a whole value, a.k.a. a "correct" value (binary level), but by no means it will ensure that it is the current and "correct" value for your business logic (it could be a past value and invalid by your logic).
  • abdullahselek
    abdullahselek about 7 years
    strong comes with ARC, retain was default before ARC
  • Rob
    Rob about 5 years
    Link is broken. ;(
  • Swati
    Swati over 4 years
    Your answer is so great. I got the complete meaning of atomic and nonatomic for the first time. I am going for an interview. Thanks a lot.
  • tipycalFlow
    tipycalFlow about 4 years
    That's the problem with links :( luckily, I've quoted the relevant text in my answer
  • Rob
    Rob about 4 years
    Durai, FWIW, that link directly contradicts your thesis of “Atomic = thread safety”. In the doc Apple explicitly says, “Property atomicity is not synonymous with an object’s thread safety.” In practice, atomic is rarely sufficient to achieve thread safety.
  • Alex Nazarov
    Alex Nazarov about 3 years
    Mutex stands for "mutual exclusion", not "mutable"
  • Cristik
    Cristik almost 3 years
    Atomic does not necessarily guarantees thread safety. Think about adding elements to a @property(atomic) NSArray *names. While accessing the array property is thread safe, modifying it is not at all thread safe.
  • Mike.R
    Mike.R over 2 years
    @bbum could u please add a thread-safe definition related to ios(or any reference to such doc)