Get Objective C Properties Right in Your iPhone Apps

I am writing this post because I see a lot of people getting their properties and accessor methods wrong in code they post to stackoverflow.com; usually it is not the only or most direct cause of the problem they are reporting but it never helps matters.

Rule 1: Never use @property(assign) for a property with object type

The reason for this rule is simple: if you do not retain or copy an object you have no control over when it is deallocated. If you use @property(assign) then you have no reason to expect that object reference to be valid when control is next passed to code in your object.

Rule 2: Set all object-type properties to nil in the dealloc method

Again, this rule has a simple reason behind it: if an object-type property has a non-nil value it has been retained, and will therefore be leaked if it is not released when the object which owns it is deallocated. If you have defined instance variables to correspond to the properties you can invoke the release method on them directly; I prefer to invoke setBlah:nil instead so that I can easily switch between roll-my-own and synthesised accessors.

Disappointingly there is no warning about forgetting to do this from the compiler or the clang static analyser; hopefully it is on the clang developer’s to-do list, as it’s the biggest cause of memory leaks left after you fix everything clang finds.

Rule 3: Use @synthesize if it’s suitable

This saves the effort of coding the accessor methods, and guarantees that the accessors will meet the contract specified in the @property declaration.

Under what common circumstances are the synthesised implementations unsuitable?

  • All of the setter methods are unsuitable if you want to validate the values which can be assigned to the property: for instance to verify that the object being assigned implements a specific method that you need; or more commonly when the value is an array type to check that it has the right number of entries.
  • The copy setter methods are unsuitable if you want a mutable copy of the object because the copy method returns an immutable copy. If you invoke copy on a NSMutableArray the result is an immutable NSArray; if you invoke copy on an NSMutableString the result is an immutable NSString.
  • The copy getter methods are unsuitable if you have a mutable copy internally which you do not want anyone else to be able to mutate.

There may be other scenarios as well, you need to consider each property on a case-by-case basis. In order to do that you need to understand what the synthesised accessors actually do. Consider this class for example:

@interface democlass
{
  NSObject *rw_retain_atomic;
  NSObject <NSCopying> *rw_copy_atomic;
  NSObject *rw_retain_nonatomic;
  NSObject <NSCopying> *rw_copy_nonatomic;
}
@property(readwrite,retain)NSObject *rw_retain_atomic;
@property(readwrite,copy)NSObject <NSCopying> *rw_copy_atomic;
@property(readwrite,retain,nonatomic)NSObject *rw_retain_nonatomic;
@property(readwrite,copy,nonatomic)NSObject <NSCopying> *rw_copy_nonatomic;
@end

@implementation democlass
@synthesize rw_retain_atomic, rw_copy_atomic, rw_retain_nonatomic, rw_copy_nonatomic;
@end

The synthesized methods are equivalent to these implementations:

-(NSObject *)rw_retain_atomic
{
  NSObject *rv;
  @synchronized(self)
  {
    rv = [[rw_retain_atomic retain] autorelease];
  }
  return rv;
}

-(NSObject <NSCopying> *)rw_copy_atomic
{
  NSObject *rv;
  @synchronized(self)
  {
    rv = [[rw_copy_atomic retain] autorelease];
  }
  return rv;
}

-(NSObject *)rw_retain_nonatomic
{
  return [[rw_retain_nonatomic retain] autorelease];
}

-(NSObject <NSCopying> *)rw_copy_nonatomic
{
  return [[rw_copy_nonatomic retain] autorelease];
}

-(void)setRw_retain_atomic:(NSObject *)new
{
  NSObject *old;
  [new retain];
  @synchronized(self)
  {
    old = rw_retain_atomic;
    rw_retain_atomic = new;
  }
  [old release];
}

-(void)setRw_copy_atomic:(NSObject <NSCopying> *)new
{
  NSObject *old;
  new = [new copy];
  @synchronized(self)
  {
    old = rw_copy_atomic;
    rw_copy_atomic = new;
  }
  [old release];
}

-(void)setRw_retain_nonatomic:(NSObject *)new
{
  NSObject *old = rw_retain_nonatomic;
  [new retain];
  rw_retain_nonatomic = new;
  [old release];
}

-(void)setRw_copy_nonatomic:(NSObject <NSCopying> *)new
{
  NSObject *old = rw_copy_nonatomic;
  new = [new copy];
  rw_copy_nonatomic = new;
  [old release];
}

Rule 4: Write good custom accessors

If your property is declared with retain and your custom setter does not retain it, you will end up trying to debug a very confusing error. Similarly if your code does not make a copy of a property declared as copy you will probably be surprised later when you find the value has changed underneath you. The standard routines are given above, below you will find template routines to address the more common reasons that the synthesised routines are unsuitable.

Lines marked //A should be deleted if the property is nonatomic. Lines marked //R should be deleted if the property is not retain. Lines marked //C should be deleted if the property is not copy. None of these are suitable if you are violating rule 1.

Validating setter

-(void)setProperty:(NSObject *)new
{
  NSObject *old;
  BOOL valid = NO;
  /* Validate new here, setting valid=YES if it's OK */
  if (!valid)
    [NSException raise:@"Attempted to set invalid value for property"
                format:@"%@",new
    ];
  [new retain]; //R
  new = [new copy]; //C
  @synchronized(self) //A
  { //A
    old = property;
    property = new;
  } //A
  [old release];
}

Copy setter that gets a mutable copy

-(void)setProperty:(NSObject <NSMutableCopying> *)new
{
  NSObject *old;
  new = [new mutableCopy];
  @synchronized(self) //A
  { //A
    old = property;
    property = new;
  } //A
  [old release];
}

Copy getter that returns an immutable copy

-(NSObject <NSCopying>*)property
{
   NSObject *rv;
   @synchronized(self) //A
   { //A
     rv = [property copy];
   } //A
   return [rv autorelease];
}

Leave a Reply

  • (will not be published)

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>