The How and Why of Cocoa Initializers

The How and Why of Cocoa Initializers

One of the longest ongoing controversies in the Cocoa community is how to write your init methods. More specifically, how to properly call your superclass's initializer. In the hopes of putting this controversy to rest, I want to walk through the right way to write an initializer and exactly why this is the right way.

How it must be done
It should come as no surprise that the right way to write an initializer is the Apple Way, which should be familiar to everyone reading this:

 

- init {
    if((self = [super init])) {
        // set up instance variables and whatever else here
    }
    return self;
}

 

Minor variations are of course just fine, as long as they're equivalent. For example, some people like to check self for nil and then return nil immediately to avoid putting initialization in a separate block. Some people like to break out the assignment, then check just plain self in the if statement. These all do the same thing, so it's just a matter of taste.

Call super or self?
For the sake of completeness, I'll first cover a more obvious part of this method, the call to [super init]. If you're really new to Objective-C and came from a language like C++ or Java, this may puzzle you. It's here because Objective-C initializers are just plain methods like any other method. If you want the superclass initializer to be called, then you have to call it yourself. You also have a choice here, between calling super and calling self. Which one you use depends on circumstances. If you're not sure, you can apply one of these rules of thumb:

 

  1. Call super if and only if the method name matches.
  2. Call super if and only if you're implementing your class's designated initializer. (This rule is more accurate, but sometimes harder to apply.)

 

Checking nil
And again for the sake of completeness, some may wonder why the if statement is there at all. The reason is because your superclass may fail to initialize (for example, if the parameters weren't consistent) and the standard way to indicate this is to release the object and return nil from the initializer. If you continue initializing your state after this happens, you will crash. The if statement lets you fail gracefully if your superclass failed.

The assignment
Now on to the meat of the controversy. The above is fairly obvious and I doubt anyone will disagree with it. But that assignment to self is frequently disputed. Let's run through the myths one by one, from most obvious to least obvious.

Myth: The superclass initializer will only ever return nil or self.
Fact: Many Cocoa classes return a different pointer from their initializer.

Myth: Fine, but that only happens with class clusters, and only when you don't subclass them.
Fact: It's true that class clusters don't do this to their subclasses, but other classes can and do.

Myth: But those other subclasses are things you would never want to subclass anyway, like NSColorPanel, or it only happens when you do something wrong, like subclass a singleton but fail to make that subclass the singleton.
Fact: These are indeed situations where it can happen, but it's not limited to these situations.

Myth: The superclass initializer has to return self because my initializer can only deal with instances of my class.
Fact: The superclass initializer could return a different instance of your class.

Myth: It can't return a different instance because you're not allowed to re-initialize instances.
Fact: It's true about re-initialization, but it could allocate a new instance.

Myth: But it has no reason to do that. It already has a new instance right there.
Fact: It has a good reason to do that if it wants some extra storage at the end, like if it created a dynamic subclass of your class and wants to use an instance of that subclass.

This is why the standard initializer pattern is the only one that works. Cocoa classes can and do deallocate the original instance, then allocate a new instance of the same class (or a subclass) and return it from their initializers. This is admittedly rare, but it is legal and it does happen. And that, in a nutshell, is why the standard Apple initializer pattern is the only correct way.

Conclusion
To summarize, the superclass's initializer can return one of three things, and the standard Apple pattern deals with them all:

 

  1. self (This is what you get the vast, vast majority of the time.)
  2. nil (On failure.)
  3. A new instance of your class (Rare but legitimate.)

 

Many people like to leave off the assignment and just check for nil. This works fine for cases 1 and 2 but will fail in very confusing ways for case 3.

There are actually a couple of other things that can potentially be returned:

 

  1. An instance of a different class
  2. An existing instance of your class

 

Case 4 only happens if you did something wrong, so it's not something you should be handling. Case 5 only happens if you either did something wrong, or if you subclassed a singleton. If you subclass a singleton then you should make sure your initializer can handle re-initializing the existing singleton instance. This is just good practice for singletons in general anyway.

(As an aside, the standard pattern is not necessary if you subclass NSObject directly. This is because -[NSObject init] in documented to do nothing and always return self. However, using the principle that we should always write code that's robust to changes, in this case to changes in what class you inherit from, I very much recommend using the standard pattern even for direct NSObject subclasses.)

So there you have it. This is how to write your initializers, and why you should write them that way. I hope it's clear enough not to generate dispute, but if you must, comment away.

References

  1. re: self = [super init] debate. - Ben Trumbull posts on cocoa-dev to explain some key points on the subject.
  2. NSManagedObject Class Reference - This class is an example of a class which returns a new, different instance of the class being initialized.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章