I’ve been using the plain old style [iVar release]; iVar = nil; style construct to free my variables for quite a long time in my dealloc methods.
But from an OO perspective it’s not always a good practice to use instance variables instead of properties, and on top of that if you have complex properties that encapsulate some kind of logic to access the instance variables than it makes even less sense to use the instance variables directly.
So after a while I’ve started using self.myProperty = nil; to free my variables, however it turned out that this wasn’t quite right either.
Let me explain a few things on how inheritance works. Say we have “Base” with one method named test :
@interface Base : NSObject {
}
- (void) test;
@end
@implementation Base
- (void) test {
NSLog(@"Invoked Base test.");
}
- (id) init {
if ( ( self = [super init] ) != nil ) {
[self test];
}
}
@end.
Now let’s have a second class “Inherit” that inherits from Base, and overrides the method test :
@interface Inherit : Base {
BOOL isSetUp;
}
- (void) test;
@end
@implementation Inherit
- (id) init {
if ( ( self = [super init] ) != nil ) {
isSetUp = YES;
}
}
- (void) test {
if ( !isSetUp ) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:nil userInfo:nil];
}
NSLog(@"Invoked Inherit test.");
}
As simple as it is, the Inherit class’ test method checks if the class was correctly initialized, and if everything is okay, then logs a message. There is nothing unusual in this, everything should work as expected, and if we would inherit from NSObject instead of Base then everything would be sunny.
But our base class is not NSObject, so things get complicated a bit. What’s happening is that the Inherit’s class initializer is invoked after the Base’s initializer, but the Base invokes the overridden test method, which will be invoked at a point when the Inherit class is not fully constructed.
Clearly this is a problem that could have been avoided if Base had not invoked a method in its initializer. Consequently the same problem exists if you invoke a method – or property – from dealloc.
There is really just very small chance that a subclass will override one of our properties or public methods which we use in our initializer or dealloc, so using properties in dealloc probably would never lead us to error. However theoretically speaking this could lead to errors.
So how do we solve this problem ?
First – avoid using public methods, properties in the initializers and dealloc since these can be overridden by your subclasses. Just always remember that your initializer runs before your subclass’ initializer, and your dealloc runs after your subclass’ dealloc.
You can do a macro to release your ivar, something like this:
#define RELEASE( ivar ) [ivar release], ivar = nil;
and you can use it in your dealloc method:
- (void) dealloc {
RELEASE( myVariable );
RELEASE( mySecondVariable );
[super dealloc];
}
Just a last quick note on this. When you publish your application, you should make sure that your ivars are all cleared in your dealloc, this way if you have an error in your program logic which tries accessing one of your released ivars the application would not crash.
On the other hand a crash is really what we want to happen while we are creating the application, so you could consider removing with a conditional flag the iVar = nil part during the implementation phase. What you could also do is to have your own singleton class that you set your ivars to, which will make sure to log a message if your ivar is used after your class’ dealloc has been invoked.