Atomic And Nonatomic Properties

Two weeks ago we looked at the @property directive and last week we saw how to use the @synthesize directive to tell the compiler to generate getters and setters for properties. We've covered the most commonly used attributes of the @property directive: readonly/readwrite and assign/retain/copy. Today we'll look at the nonatomic attribute and talk about what it's for and why you should (or should not) use it.

Threads and data

By default, your app's code executes on the main thread of your app's process, along with most Cocoa Touch framework code. Any particular method or function runs uninterrupted from start to finish and as long as that method or function leaves all the data it touches in a good state when it returns, your program runs correctly.

When you have multiple threads in your application, things aren't so easy. One key challenge when using multiple threads is to make sure you only read data when it's in a consistent state. This is similar in concept to using a transaction in a SQL database.

Suppose you have a custom UI object that's defined like this:

@interface MyWidget {
  CGPoint center;
  // ...
}
@property CGPoint center;
// ...
@end

@implementation

@synthesize center;

// ...
@end

If you treat instances of this class as read only when you share them between threads, you're safe. The trouble appears when one or both threads start to make changes to the object. If we were to write the getter and setter for center, it would look like this:

// example assign-type getter and setter
- (CGPoint) center {
  return center;
}
- (void)setCenter:(CGPoint)theCenter {
  center = theCenter;
}

This looks simple enough, but the compiler is helping us out here. The center instance variable is a struct that's defined like this:

// struct CGPoint
struct CGPoint {
  CGFloat x;
  CGFloat y;
};

The setCenter: method is actually doing something like this:

- (void)setCenter:(CGPoint)theCenter {
  center.x = theCenter.x;
  center.y = theCenter.y;
}

Let's look at what happens when one thread calls the setter and a second thread calls the getter. In the simple case, the setter and getter calls don't overlap:

// given MyWidget instance myWidget:

// thread 1 calls setter:
[myWidget setCenter:CGPointMake(1.0f, 2.0f)];

// setCenter method executes:
- (void)setCenter:(CGPoint)theCenter {
  center.x = theCenter.x; // 1.0f
  center.y = theCenter.y; // 2.0f
}
// center is now {1.0f, 2.0f}

// ... thread 1 preempted by thread 2 ...

                    // thread 2 calls getter:
                    CGPoint point = [myWidget center];
                    
                    // center method executes:
                    - (CGPoint) center {
                      return center; // 1.0f, 2.0f
                    }
                    
                    // point is now {1.0f, 2.0f}

In this case, we get the answer we expect. Now suppose we do this again, only thread 1 gets preempted by thread 2 in the middle of the setCenter method:

// myWidget.center is {1.0f, 2.0f}

// thread 1 calls setter:
[myWidget setCenter:CGPointMake(3.0f, 5.0f)];

// setCenter method executes:
- (void)setCenter:(CGPoint)theCenter {
  center.x = theCenter.x; // 3.0f

// ... thread 1 preempted by thread 2 ...

                    // thread 2 calls getter:
                    CGPoint point = [myWidget center];
                    
                    // center method executes:
                    - (CGPoint) center {
                      return center; // 3.0f, 2.0f
                    }
                    
                    // point is now {3.0f, 2.0f}

  center.y = theCenter.y; // 5.0f
}

// myWidget.center is now {3.0f, 5.0f}
// but thread 2 read {3.0f, 2.0f}

Now thread 2 is working off of a corrupt value and things are likely to go haywire. To solve this problem, we need to prevent all threads from reading center until the setCenter: method is finished. Because this is a common problem in multithreaded code, Objective-C has a special directive to accomplish this: @synchronized. We can rewrite our getter and setter for the center property like this:

// adding @synchronized to getter and setter
- (CGPoint) center {
  CGPoint theCenter;
  @synchronized(self) {
    theCenter = center;
  }
  return theCenter;
}
- (void)setCenter:(CGPoint)theCenter {
  @synchronized(self) {
    center = theCenter;
  }
}

Now when we read and write center from two threads, @synchronized causes other threads to pause whenever one thread is inside either of the @synchronized blocks:

// myWidget.center is {1.0f, 2.0f}

// thread 1 calls setter:
[myWidget setCenter:CGPointMake(3.0f, 5.0f)];

// setCenter method executes:
- (void)setCenter:(CGPoint)theCenter {
  @synchronized(self) {
    center.x = theCenter.x; // 3.0f

// ... thread 1 preempted by thread 2 ...

                    // thread 2 calls getter:
                    CGPoint point = [myWidget center];
                    
                    // center method executes:
                    - (CGPoint) center {
                      CGPoint theCenter;
                      @synchronized(self) {
                      // thread 1 is already synchronized on 
                      // self so thread 2 pauses here

// ... thread 2 yields and thread 1 runs again ...

    // still inside @synchronized on thread 1
    center.y = theCenter.y; // 5.0f
  }
}

// ... thread 1 preempted by thread 2 again ...

                      @synchronized(self) {
                        // now thread 2 resumes
                        theCenter = center; // 3.0f, 5.0f
                      }
                      return theCenter; // 3.0f, 5.0f
                    }
                    
                    // point is now {3.0f, 5.0f}

I'm glossing over many of the details of @synchronized here. If you're writing multithreaded code, you should read the Threading Programming Guide. The key concept here is that by default, the @synthesize directive generates this type of synchronization for you.

Atomic or nonatomic

Behind the scenes, the @synchronized directive uses a lock to prevent two threads from accessing a @synchronized block simultaneously. Although acquiring and releasing the lock is very quick, it's not free. Occasionally you have a property that is so frequently accessed that all this locking and unlocking adds up to a noticeable penalty. In these rare cases, you can declare the property to be nonatomic:

@interface MyWidget {
  CGPoint center;
  // ...
}
@property (nonatomic) CGPoint center;
// ...
@end

The compiler omits the synchronization code when generating nonatomic getters and setters. Note that there isn't a corresponding atomic attribute for @property; generated getters and setters are synchronized by default.

Don't prematurely optimize

Acquiring a lock is very fast in the common case where no other thread is holding it. According to Apple's docs, it takes about 0.0000002 seconds (that's 0.2 microseconds) on a modern Mac. Even though the iPhone is much slower, you need to be acquiring locks hundreds of thousands of times before you should consider synchronization overhead as anything significant. For the vast majority of code, you should simply not even worry about nonatomic.

Also, keep in mind that the attributes you set on your @property declarations only apply when you use @synthesize to have the compiler generate the getter and setter methods. If you write the getter or setter yourself, the attributes are ignored. Next week we'll look a little more at synchronization and show you how to write a thread safe getter when returning an Objective-C object.