Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

objective-c

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?

like image 744
fphilipe Avatar asked Aug 17 '12 07:08

fphilipe


1 Answers

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;
}
like image 106
rob mayoff Avatar answered Sep 19 '22 16:09

rob mayoff