Tuesday, May 18, 2010

Objective-C Tuesdays: changing default property names

Last week we looked at declaring atomic and nonatomic properties. I promised to talk about writing a thread safe getter this week, but I'm pressed for time today so I'm going to shoe horn in another short topic instead.

When you declare a property in a class, the getter has the same name as the property, while the setter is the capitalized property name with set prepended. For illustration, here is a User class with a property named active:
@interface User : NSObject {
  BOOL active;
}
@property BOOL active;

@end


@implementation User

@synthesize active;

@end
The compiler will generate a getter method named active and a setter method named setActive. Sometimes the default method names aren't exactly what you want; for instance, it's a common convention that getters for BOOL properties be prefixed with is. In the case of our User class, we would like to have the getter named isActive.

The @property directive has two attributes to do just this: getter=name and setter=name. Just like the other attributes of @property, getter and setter are placed in parentheses after the @property directive and before the property type and name. So the active property in our example would look like:
@interface User : NSObject {
  BOOL active;
}
@property (getter=isActive) BOOL active;

@end

@implementation User

@synthesize active;

@end
And usage the active property now looks like:
// example use of isActive getter
User *user = [[User alloc] init];

// getter/setter called as methods
if ( ! [user isActive] ) {
  [user setActive:YES];
}

// getter/setter called with dot syntax
if (user.isActive) {
  user.active = NO;
}
We'll take a closer look at dot syntax in the near future, but basically when the compiler sees code like this:
if ( user.isActive ) {
  user.active = NO;
}
it translates it into this:
if ( [user isActive] ) {
  [user setActive:NO];
}
Dot syntax is complementary to properties, but is separate and unaware of them. When translating a setter call in dot syntax to the corresponding method call, the compiler always looks for a method beginning with set. If you change your setter name to something different, you'll see a compiler error like
object cannot be set - either readonly property or no setter found
or
request for member 'setter name' in something not a structure or union
if you call it using dot syntax. So if we rename the setter for the active property to makeActive instead of the default setActive:
@interface User : NSObject {
  BOOL active;
}
@property (getter=isActive, setter=makeActive) BOOL active;

@end
then using method call syntax, we can now do this:
if ( [user isActive] ) {
  [user makeActive:NO];
}
but because the compiler expects setters to begin with set, using dot syntax generates an error:
if (user.isActive) {
  user.makeActive = NO; // ERROR: request for member 'makeActive' 
                               // in something not a structure or union
}
In general you should follow the standard conventions and use the default getter and setter names; it's less work for you and your code is easier for others to understand.

Next week, we'll look at the thread safety of getters that return retained objects.

2 comments:

ideeConfuse said...

Great post!

Thanks a lot

Don McCaughey said...

Thanks! Glad you found it useful.