fphilipe
fphilipe

Reputation: 10054

Why check self != nil in -init when messaging nil has no effect?

Assuming our -init method only invokes messages on self, why is it common to check if self != nil if messaging nil has no effect?

Let's say we have an initializer as follows:

- (id)init
{
    self = [super init];
    if (self) {
        [self doThis];
        [self setFoo:@"Bar"];
    }

    return self;
}

Instead of checking self, we could write:

- (id)init
{
    self = [super init];
    [self doThis];
    [self setFoo:@"Bar"];

    return self;
}

Now if for some reason [super init] returns nil, there would be no difference in the outcome of the method as far as I know. Why then do we constantly perform this check?

Upvotes: 6

Views: 419

Answers (2)

rob mayoff
rob mayoff

Reputation: 385870

You can send a message to nil, but you cannot access the instance variables of nil. You'll get an EXC_BAD_ACCESS exception.

Consider a class that has instance variables:

@implementation MyObject {
    int instanceVariable;
}

- (id)init {
    self = [super init];
    instanceVariable = 7;
    return self;
}

What happens if [super init] returns nil in this example? You will try to access that instanceVariable off of a null pointer and you will get an exception.

Even if you're not accessing any instance variables, other things can certainly go wrong if you don't check for self == nil.  You can easily leak malloc-allocated memory or file handles, or pass yourself to some method that's not expecting nil.

Other answers claim that you can leak objects if you don't check for nil. For example:

@implementation MyObject

@synthesize someProperty; // assume it's an NSObject *

- (id)init {
    self = [super init];
    [self setSomeProperty:[[NSObject alloc] init]];
    return self;
}

This won't leak under ARC, even if self is nil. Under manual reference counting (MRC), this example will leak whether self is nil or not, because there's nothing to balance the +1 retain count from [NSObject alloc].

The proper way to do it under MRC is this:

- (id)init {
    self = [super init];
    [self setSomeProperty:[[[NSObject alloc] init] autorelease]];
}

or this:

- (id)init {
    self = [super init];
    NSObject *object = [[NSObject alloc] init];
    [self setSomeProperty:object];
    [object release];
    return self;
}

Neither of those will leak, whether self is nil or not.

If you bypass the setter method, like this, you'll just crash if self is nil:

- (id)init {
    self = [super init];
    _someProperty = [[NSObject alloc] init];
    return self;
}

Upvotes: 7

user1606088
user1606088

Reputation: 11

If [super init] did in turn return nil, then you would end up possibly allocating more objects that will never be used, since when you return self, you will return nil; So those objects will not be released.

Upvotes: 0

Related Questions