Use Your Loaf

[[brain engage] write]

Automatic Property Synthesis With Xcode 4.4

The release of OS X 10.8 Mountain Lion this week also brought us an Xcode release with some long awaited Objective-C improvements. If you upgraded your development machine to Mountain Lion you should check the Mac App Store for the Xcode 4.4 update. If you are more cautious and are staying with OS X 10.7 Lion you can still upgrade to Xcode 4.4 (according to the release notes you will need to be on at least 10.7.4).

One of the reasons that you may want to upgrade to Xcode 4.4 is that it includes Apple LLVM compiler 4.0 that adds a number of new Objective-C Language features. Even better most of the new features are backwardly compatibile with older versions of iOS. To make is easier to understand which features are available for which tool and OS releases Apple has published a useful Objective-C Feature Availability Index.

Default Synthesis Of Properties

The new Objective-C feature that I want to examine in this post is the default synthesis of properties. Here is the description from the Clang Language Extensions web page:

Clang provides support for autosynthesis of declared properties. Using this feature, clang provides default synthesis of those properties not declared @dynamic and not having user provided backing getter and setter methods.

To understand what this means consider a typical class interface implemented with LLVM Compiler 3.x that I used in a recent code example:

@interface UYLCountryViewController : UIViewController
@property (nonatomic, strong) Country *country;
@end

This class has a public property named country defined in the class interface backed by an instance variable that is synthesized in the class implementation:

@implementation UYLCountryViewController
@synthesize country=_country;

The @synthesize directive tells the compiler to generate the getter and setter accessor methods for us saving a lot of error prone typing. In this case I also chose to name the instance variable using the common naming convention of prefixing the property with an underscore. As we will see shortly that turns out to be a fortunate choice. Of course, even though we asked the compiler to generate the accessor methods we can still choose to provide our own implementation if we want to. This gives us the flexibility to specify a custom getter, for example, and still have the compiler generate the setter or vice versa.

Eliminate Code With Autosynthesis

With Xcode 4.4 and LLVM Compiler 4.0 the @synthesize directive is no longer required as it will be provided by default. That means in most cases you now only need the @property and the compiler takes care of everything else for you. As before you can place the @property directive in the class interface if you want it to be public or in a private class extension in the class implementation file. Either way if you do nothing else the compiler will add an instance variable and implementations for the required accessor methods for you. By default the instance variable is named by prefixing the property name with an underscore.

To give another example, if you have a property named person the compiler will produce code as if you had written the following:

@synthesize person=_person;

The accessor methods that are created by the compiler will as always implement the accessor semantics specified in the @property statement. So if you specify a readonly property the compiler will not for example generate a setter method. This really helps remove all of the boilerplate code previously required by Objective-C when defining instance variables.

Naming Conventions For Instance Variables

There are some subtle implementation issues you need to be aware of before you delete all of the @synthesize statements from your existing code. Most importantly a lot will depend on how you have been naming your instance variables. Now I should say that even for the relatively short period of time that I have been using Objective-C I have seen a lot of conflicting advice over how you should name instance variables.

One piece of “advice” that often gets mentioned is that you should not use a leading underscore for your instance variables as it is reserved by Apple. However since a lot of Apple sample code and Xcode templates use a leading underscore and I have never found an Apple statement to that effect I long ago adopted that convention. (Also see WWDC 2012 session 405 where an Apple engineer confirms that the leading underscore is not reserved by Apple for instance variable names.) Hence all of my @synthesize statements tend to look like this:

@synthesize myIvar=_myIvar;

This now turns out to have been a good choice since this is exactly what the autosynthesis of properties assumes. This means in most simple cases you can simply remove the @synthesize statement.

When To Supply The Instance Variable

Most of the time the compiler will add the ivar for you when it adds the required accessor methods. However there are certain situations where you will still need to add the instance variable for yourself. In general the compiler will generate the instance variable automatically any time it generates at least one of the accessor methods. Some examples should make this clearer:

Readonly Property With Non-Default Getter

This first example is the case where you have a readonly property such as the following example:

@property (readonly) NSArray *myArray;

If you supply your own implementation of the getter accessor method you might write something like this:

- (NSArray *)myArray
{
  return _myArray;
}

This however generates a compiler error complaining that ‘_myArray’ is an undeclared identifier. In the case of a readonly property there is only one accessor method, the getter. If we supply our own implementation the compiler makes the assumption that we are taking complete control of the implementation details and does not supply the instance variable. This flexibility also allows for those situations where you declare a property which is not backed by an instance variable.

To remove the compiler error you can just declare the instance variable yourself or request the compiler to do it for you with the @synthesize statement:

@synthesize myArray=_myArray;

Note that if you add the @synthesize statement you need to make sure that the name of the instance variable is consistent with the name used in the accessor method. In the getter method we reference _myArray so we need to specify that name in the @synthesize statement. By default if you do not specify a name for the instance variable the @synthesize statement will use the property name (without the leading underscore).

So the following would still generate a compiler error as it would create an instance variable named myArray and not the _myArray that we are using in the getter:

@synthesize myArray;  // equivalent to @synthesize myArray=myArray;

Readwrite Property With Non-Default Getter and Setter

This situation is similar to the readonly property with our own getter method. If we have a readwrite property the compiler will not create the instance variable if we implement both the getter and setter methods. So if we declare the following property (readwrite is the default if not specified):

@property (strong, nonatomic) NSNumberFormatter *decimalFormatter;

We can declare our own getter method to lazily instantiate the number formatter as shown below and the compiler will supply both the setter and the instance variable (named _decimalFormatter) for us automatically.

- (NSNumberFormatter *)decimalFormatter
{
  if (!_decimalFormatter)
  {
    _decimalFormatter = [[NSNumberFormatter alloc] init];
    [_decimalFormatter setNumberStyle:NSNumberFormatterDecimalStyle];        
  }
  return _decimalFormatter;
}

However if we also supply an implementation for the setter, -(void)setDecimalFormatter we will get a compiler error telling us that the _decimalFormatter is undeclared. In that situation we again need to go back to telling the compiler to synthesize the variable for us paying attention to the name:

@synthesize decimalFormatter=_decimalFormatter;

Protocols

I had not originally considered how properties declared as part of a protocol are impacted by this new behaviour. Luckily I saw this tweet from Graham Lee (@secboffin) yesterday which provides the answer:

“Warning: auto property synthesis will not synthesize a property declared in a protocol.” Nice to know.

What this means if that if we have a protocol that includes a property such as the following:

@protocol someProtocol <NSObject>
@property (nonatomic, strong) NSString *name;
@end

Any class that adopts this protocol will need to explicitly declare or synthesize the accessors and instance variable for the name property. The compiler generates a warning on the class implementation as a reminder in case you forget.

Atomic Properties

This is nothing new and does not change the rules about instance variable synthesis but I think it is worth recalling what happens with atomic properties. The getter and setter methods for an atomic property need to ensure that they do not interfere with each other when called from different threads. For example, the getter method should not be able to retrieve a value whilst the setter is in the process of setting it. This synchronisation means that you cannot implement one of the accessors and have the compiler supply the other.

I mention it here in passing as the issue can catch you out since properties are atomic by default. So for example if we declare the following property:

@property (strong) NSString *name:

This is actually an atomic property so if we add our own implementation of the getter method but do not also supply the setter we will get a compiler warning:

Writable atomic property 'name' cannot pair a synthesized setter with a user defined getter

The solution is to either implement the setter or change the property to nonatomic (assuming you do not need the accessors to be thread-safe).

@property (nonatomic, strong) NSString *name:

Converting Existing Code

There is no reason why you have to go back and start deleting @synthesize statements from existing code since the default action maintains backwards compatibility. Of course if you enjoy deleting code or you are modifying a class it might be an idea to clean things up when possible.

I came across a good example of the issues you can get with existing code whilst playing around with the WorldFacts project I created recently to experiment with storyboards. This project was created using an Xcode template and includes core data. You can refer to the project in my CodeExamples repository for the final result but below are my notes on attempting to remove all of the @synthesize statements from this project:

UYLAppDelegate

The application delegate defines properties for the core data stack and also the UIWindow:

@property (strong, nonatomic) UIWindow *window;
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;

The class implementation includes @synthesize statements for each property but whoever created the template decided to use a double underscore to prefix the core data instance variable names (but not the window):

@synthesize window = _window;
@synthesize managedObjectContext = __managedObjectContext;
@synthesize managedObjectModel = __managedObjectModel;
@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;

The _window instance variable is never directly accessed so we can safely delete the first @synthesize statement and let the compiler handle the details.

The Xcode template follows the common pattern of lazily instantiating the core data stack in the getter method for each of the core data properties. I will not show the code here but they obviously refer to an instance variable with a double underscore (e.g. __managedObjectContext). This could cause an issue if we were relying on the compiler to synthesize the instance variable. However in this case we are dealing with a readonly property where we implement the getter so the compiler will leave us alone. This means we have to leave the @synthesize statement to explicitly request the compiler generate the instance variable. You could also rename the variables to use a single underscore but in this case I saw little point in making the change.

So in this case I only managed to remove one of the @synthesize statements.

UYLCountryTableViewController

The table view controller (UYLCountryTableViewController) has three instances of the @synthesize statement:

@synthesize managedObjectContext=__managedObjectContext;
@synthesize fetchedResultsController=__fetchedResultsController;
@synthesize decimalFormatter=_decimalFormatter;

Here both the managedObjectContext and fetchedResultsController properties use a double underscore for the ivar name. In addition we implement getter methods for both the fetchedResultsController and decimalFormatter properties.

The managedObjectContext ivar is never directly accessed in the controller so we can delete the @synthesize statement without impact. Note that this does actually change the name of the ivar but since we are using compiler generated accessors it does not cause us a problem.

If we delete the @synthesize statement for the fetchedResultsController property we will get a compiler error in our getter implementation as the __fetchedResultsController ivar will then be undefined. In this case I think it makes sense to rename the ivar in the getter to _fetchedResultsController and delete the @synthesize.

Finally we can delete the @synthesize statement for the decimalFormatter since we are already using the default ivar name in our getter implementation.

UYLCountryViewController

The remaining view controller in the project (UYLCountryViewController) is much simpler. It has one public property and a number of private properties all with compiler synthesized accessors. There are no accessor methods or direct accesses to any of the instance variables in our code. In this case we can simply delete all of the following @synthesize statements:

@synthesize country=_country;
@synthesize area=_area;
@synthesize capital=_capital;
@synthesize continent=_continent;
@synthesize currency=_currency;
@synthesize phone=_phone;
@synthesize population=_population;
@synthesize tld=_tld;

Wrapping Up

I like any improvements to the compiler that result in us having to write less code. Compared to a couple of years ago a lot of the boilerplate code from class interfaces is now no longer required. We are now at the point where apart from some special cases the only code required to add instance variables to a class is the @property statement.

Comments