2011-10-12

Another Objective-C Gotcha - Synthesized Properties and Ivars

When synthesizing ivars and properties, Objective-C will give both the same name. Unless explicitly told to use the property by prefixing it with self., Objective-C will use the related ivar instead when referenced within the class.

I spent about two hours trying to track down the problem in the code below. It’s obvious now that I know what the problem is, but trying to isolate it from the rest of the application was tricky. The fact that the code in question was interacting with a block, a cause of much memory-related pain in the past, threw me off the scent for a while.

This is the interface for the class in question:


@interface Person : NSObject

@property (copy) NSString* name;

- (id)init;
- (id)initWithName:(NSString*)aName;
- (void)dealloc;

@end

This is the implementation:


@implementation Person

@synthesize name;

- (id)init {
    [self initWithName:nil];
}

- (id)initWithName:(NSString*)aName {
    name = aName;
}

- (void)dealloc {
    [name release];
}

@end

Here’s how we’d use it:


Person* bob = [[Person alloc] initWithName:@"Bob"];

NSLog(@"%@", bob.name);

This looks fine, but it contains a subtle bug. Trying to retrieve bob.name will result in a crash once the current autorelease pool has been drained. The synthesized property name and its backing synthesized ivar are both called name. When we set name equal to aName in the initWithName: method, we are setting the ivar, not the property. Thus, we bypass the copy logic that we’ve set up in the property and just change a pointer. The string literal @"Bob" is autoreleased, so we’ve got a pointer to a deallocated object.

There are two ways to avoid this mistake:

  • Always declare ivars explicitly, using a name that is different from the property name (ie. _name);
  • Always prefix property names with self. when referring to them.

Comments

Jeff on 2011-10-12 at 11:17 said:

Actually, I have this nagging suspicion there is an Apple guideline that says “don’t use property accessors in init or dealloc methods”, though there’s lots of debate around the interwebs about it.

Big picture, my attitude is “if Apple say don’t do it, don’t do it”.

Ant on 2011-10-12 at 12:23 said:

Good point. I’ve seen that somewhere myself.

Andres on 2012-06-05 at 08:22 said:

Thank you for the explanation. But I remember reading somewhere that, if you declare a property directly without declaring an ivar in the .h, the compiler will make it for you BUT this depends on some factors: compiler, processor -32 or 64 bits-, iOS version… ┬┐Can anyone please confirm it? Because I prefer programming this way, but I’m not sure if I really should. I mean, if this can make an app to run in some devices and not to run in others, I’d avoid doing it.

Thank you in advance!