views:

1939

answers:

6

I'm from the C++ world so the notion of assigning this makes me shudder:

this = new Object; // Gah!

But in Objective-C there is a similar keyword, self, for which this is perfectly acceptable:

self = [super init]; // wait, what?

A lot of sample Objective-C code uses the above line in init routines. My questions:

1) Why does assignment to self make sense (answers like "because the language allows it" don't count)

2) What happens if I don't assign self in my init routine? Am I putting my instance in some kind of jeopardy?

3) When the following if statement fails, what does it mean and what should I do to recover from it:

- (id) init
{
    self = [super init];

    if (self)
    {
        self.my_foo = 42;
    }

    return self;
}
+12  A: 

This is a topic that is frequently challenged by newcomers:

Dave DeLong
Good answer. In addition, when it comes to Objective-C, I always trust **@bbum**... http://stackoverflow.com/questions/1287950/#1289199
Quinn Taylor
+1  A: 

I'm still new to Objective C, but this post helped me in understanding this.

To sum it up, most init calls return the same object that self is already initialized to. If there is an error, then init will return nil. Also, some objects such as singletons or unique objects (like NSNumber 0) will return a different object than the one initialized (the singleton or a global 0 object). In these situations you need to have self reference that object. I'm by no means an expert in what is going on behind the scenes here, but it makes sense on the surface, to me.

Barry
+5  A: 

In Objective-C, initializers have the option of returning nil on failure or returning a completely different object than the one the initializer was called on (NSArray always does this, for example). If you don't capture the return value of init, the method might be executing in the context of a deallocated object.

Some people disagree about whether you should do the whole assign-to-self rigamarole if you don't expect to get something else back from the superclass initializer, but it's generally considered to be good defensive coding.

And yes, it looks weird.

Chuck
+2  A: 

All the other points here are valid, but it's important for you to understand as well that self is an implicit parameter to every Objective-C method (objc_msgSend() passes it) and can be written to, just like any other method parameter. (Writing to explicit parameters is generally frowned upon, unless they are out parameters.)

Typically, this is only done in the -init method, for the reasons others have stated. It only has any effect because self is returned from the method and used in the assignment id obj = [[NSObject alloc] init]; It also affects the implicit resolution of ivars, because, for example, if myVar is an ivar of my class, then accessing it in a method causes it to be implicitly resolved to self->myVar.

kperryua
+1  A: 

If [super init] returns nil that means that you have been deallocated and your self parameter is now an invalid pointer. By blindly following the self = [super init] convention you will save you from potentially nasty bugs.

Consider the following non-typical initializer:

- (id)initWithParam:(id)param {
    if (!param) {
        // Bad param.  Abort
        self = [super init]; // What if [super init] returns nil?
        [self release];
        return nil;
    }
    else 
    {
        // initialize with param.
        ...
    }
}

Now what happens if my superclass decides to abort and return nil? I have been de-allocated and my self parameter is now invalid and [self release] will crash. By re-assigning self, I avoid that crash.

Darren
+3  A: 

It is true that init may return nil, if the initialization fails. But this is not the primary reason why you should assign to self when you implement your own initializers.

It has been mentioned before, but it is needed to stress even harder: the instance returned from an initializer may not be the same instance as the one you sent in, in fact it may not even be of the same class!

Some classes use this as a standard, for example all initializer to NSString and NSArray will always return a new instance of a different class. Initializers to UIColor will frequently return a different instance of a specialized class.

And you yourself can happely implement something like this if you want:

-(id)initWithName:(NSString*)name;
{
  if ([name isEqualToString:@"Elvis"]) {
    [self release];
    self = [[TheKing alloc] init];
  } else if (self = [super init]){
    self.name = name;
  }
  return self;
}

This allows you to break out the implementation of some special case into a separate class, without requiring the clients of your API to care or even know about it.

PeyloW