29

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;
}
fbrereto
  • 35,429
  • 19
  • 126
  • 178
  • 10
    Why are people so surprised when different languages have different conventions and different rules? If they didn't, they'd all be the same. – Terry Wilcox Aug 27 '09 at 15:45
  • 1
    In this particular case Objective-C is closely related to C and C++, and the notion of assigning `this` in those languages has a very different connotations than assigning `self` – fbrereto Aug 27 '09 at 16:13
  • 6
    Objective-C is closely related to C, not to C++. It is totally unrelated to C++ and therefore has a different convention for dealing with objects and for initialization of objects. – Jason Coco Aug 27 '09 at 17:28
  • Irrelevant to your question, but your example init method should return self... – Barry Wark Aug 27 '09 at 17:36
  • I ususally do `if ((self = [super init])) { ... }` (double brackets to indicate that that '=' is intentionally not '=='. – mk12 Oct 01 '09 at 00:02

6 Answers6

35

This is a topic that is frequently challenged by newcomers:

Basically, it stems from the idea that a superclass may have over-ridden the designated initializer to return a different object than the one returned from +alloc. If you didn't assign the return value of super's initializer into self, then you could potentially be dealing with a partially initialized object (because the object that super initialized isn't the same object that you're initializing).

On the whole, it's pretty rare for super to return something different, but it does happen in a couple of cases.

Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • 3
    Good answer. In addition, when it comes to Objective-C, I always trust **@bbum**... http://stackoverflow.com/questions/1287950/#1289199 – Quinn Taylor Aug 27 '09 at 17:35
  • 2
    You have made the faulty assumption that nothing on the web ever changes and that these links will forever be helpful. The web does change, half of your links are broken. Please, when answering questions, ANSWER THE QUESTION. Quoting things and sourcing them with links is fine, but actually write out an answer! – ArtOfWarfare Dec 30 '12 at 05:36
11

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
  • 234,037
  • 30
  • 302
  • 389
6

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
  • 36,742
  • 12
  • 80
  • 99
3

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.

Quinn Taylor
  • 44,553
  • 16
  • 113
  • 131
kperryua
  • 10,524
  • 1
  • 38
  • 24
1

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
  • 2,053
  • 3
  • 17
  • 28
1

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
  • 25,520
  • 5
  • 61
  • 71