Pitfalls of Objective-C, Part I - Properties

I personally think that properties in Objective-C 2.0 are some pretty nice improvement for the language. It helps you creating less code by using a more “natural” dot-syntax instead of the good ol' getter and setter methods:

Person *person = [[Person alloc] init];

// Without Objective-C 2.0 properties
[person setName:@"John Appleseed"];
NSLog(@"The person's name is: %@", [person name]);

// With Objective-C 2.0 properties
person.name = @"John Appleseed";
NSLog(@"The person's name is: %@", person.name)

To create a property, simply use the @property directive that was introduced in Objective-C 2.0 in your Person.h file.

@interface Person : NSObject {

 NSString *name;
}

@property (nonatomic, retain) NSString *name;

@end

Take a look at the retain keyword right behind the @property directive. It means that every new value that will be assigned by using properties will be retained while the old value will be released. This is very important for the iPhone’s memory managment (as you will see in a few moments). After you defined your property in your Person.h file, head over to the Person.m file and “synthesize” the actual getter and setter methods by using the @synthesize directive

@implementation Person

@synthesize name;

//...

(void) dealloc {

 [name release];
 [super dealloc]
}

@end

Now, I’m actually working on an iPhone project where I’m having a UIViewController called rootViewController that has another UIViewController, let’s call it otherViewController, as a property. otherViewController is a sublassed UIViewController that controls a simple UIView with an UIButton to trigger an arbitrary action. When rootViewController is created, I’m using the controller’s viewDidLoad: method to create and initialize otherViewController. Here’s the (wrong) code I used first:

- (void)viewDidLoad {

 [super viewDidLoad];

 OtherViewController *oc = [[OtherViewController alloc] initWithNibName:@"OtherViewController" bundle:nil];
 otherViewController = oc;
 [self.view addSubview:oc.view];
 [oc release];
}

The result was that my app kept on crashing everytime I tried to trigger the button in the UIView controlled by otherViewController. It took me about two (very frustrating) hours to figure out where I made the mistake. It turned out that it was right in viewDidLoad:. After I changed

otherViewController = oc;

into

self.otherViewController = oc;

everything went well.

Before I started developing in Objective-C, I spent some years on Java. So if you’re a Java developer you might say, “I don’t see the point. Those two statements look equal.” – Yeah, you’re right – from a Java perspective. In Java the two statments

foo = someValue;

and

this.foo = someValue;

are equal if foo is a member variable of some arbitrary class, but for Objective-C you’re screwed with that.

Take a look at the second statment: self.otherViewController indicates that you’re using Objective-C 2.0 properties. When you’re assigning oc to otherViewController, it uses the retain and release: The old value for otherViewController is released and the new value (oc) is retained. By retaining variables in Objective-C you make sure that the corresponding objects will stay in memory until you tell them to leave. Internally, all Objective-C objects use a retain count. By sending a release message to that object, you’re telling it, “Goodbye, I don’t need you anymore.” and the retain count is decreased by 1. After the retain count reaches 0, the corresponding object will be de-allocated from memory.

Now take a look at the first stament. As I said before, It pretty looks the same except for “self.” but it isn’t. This is just a simple assignment without using retain and release. If you’re not using retain, the concerning object will never know that you need it. The trouble comes right at the last line

[oc release];

oc is sent a release method and the object will eventually be de-allocated from memory. But what about otherViewController? Where is he pointing to after oc is de-allocated? – You can never tell. The app itself decides how to use those new freed blocks of memory. If you try to access those blocks (like me by triggering the button), your app will crash with some very weird error messages like

2009-07-17 15:49:35.929 SomeApp[19176:20b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[UITextEffectsWindow triggerButton:]: unrecognized selector sent to instance 0xd1a270'

So, do yourself a favour and think about the differences of self.foo = ... and foo = ... – It might save you from some very frustrating hours ;)

Filed under  //   development   objective-c  

About

Addicted to shiny apples, beautiful pixels, awesome code and electronic music.

TwitterFacebookFlickr