166

I have a general question about writing init methods in Objective-C.

I see it everywhere (Apple's code, books, open source code, etc.) that an init method should check if self = [super init] is not nil before continuing with initialisation.

The default Apple template for an init method is:

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

    if (self != nil)
    {
        // your code here
    }

    return self;
}

Why?

I mean when is init ever going to return nil? If I called init on NSObject and got nil back, then something must be really screwed, right? And in that case, you might as well not even write a program...

Is it really that common that a class' init method may return nil? If so, in what case, and why?

Jasarien
  • 58,279
  • 31
  • 157
  • 188
  • 5
    I had seen this question a while ago and just found it again. Perfect. +1 – Dan Rosenstark Jul 09 '10 at 01:53
  • This looks very odd. [super init] sounds like init its super class. Why assign it to subclass? Why is it not if(self = [self init])? Anyone can help? Thanks Jamie –  Feb 27 '12 at 21:14
  • 1
    Wil Shipley posted an article related to this a while back. [self = [stupid init];](http://www.wilshipley.com/blog/2005/07/self-stupid-init.html) Read through the comments as well, some good stuff. – Ryan Townshend Aug 17 '09 at 13:30
  • 6
    You could ask [Wil Shipley](http://www.wilshipley.com/blog/2005/07/self-stupid-init.html) or [Mike Ash](http://www.mikeash.com/?page=pyblog/the-how-and-why-of-cocoa-initializers.html) or [Matt Gallagher](http://cocoawithlove.com/2009/04/what-does-it-mean-when-you-assign-super.html). Either way, it's something of a debated topic. But usually it's good to stick with Apple's idioms... it's their Frameworks, after all. – jbrennan Aug 17 '09 at 13:30
  • 1
    It seems Wil was making a case more for not blindly reassigning self during init, knowing that [super init] may not return the receiver. – Jasarien Aug 17 '09 at 15:22
  • 3
    Wil has changed his thoughts since that post was originally made. – bbum Aug 17 '09 at 18:35
  • +1 bravo, great links. Man I have to get back to writing code and stop reading all this stuff. But first let me note that NONE of the authors is suggesting not using the `if`. But @Jasarien already said that, pretty much... – Dan Rosenstark Jul 09 '10 at 02:03
  • what bbum said is exactly correct. – nielsbot Jan 14 '13 at 02:54

9 Answers9

54

For example:

[[NSData alloc] initWithContentsOfFile:@"this/path/doesn't/exist/"];
[[NSImage alloc] initWithContentsOfFile:@"unsupportedFormat.sjt"];
[NSImage imageNamed:@"AnImageThatIsntInTheImageCache"];

... and so on. (Note: NSData might throw an exception if the file doesn't exist). There are quite a few areas where returning nil is the expected behaviour when a problem occurs, and because of this it's standard practice to check for nil pretty much all the time, for consistency's sake.

iKenndac
  • 18,730
  • 3
  • 35
  • 51
  • 10
    Yes, but this isn't INSIDE the respective class' init method. NSData inherits from NSObject. Does NSData check if [super init] returns nil? That's what I'm asking here. Sorry if I wasn't clear... – Jasarien Aug 17 '09 at 13:33
  • 9
    If you were to subclass those classes, there would be a pretty real chance that [super init] will return nil. Not all classes are direct subclasses of NSObject. There's never any guarantee that it will not return nil. It's just a defensive coding practice that's generally encouraged. – Chuck Aug 17 '09 at 13:38
  • 2
    @Jasarien: It CAN be done inside your `super`'s init method, which you are calling. Sure, NSObject probably won't fail to init, but if you're subclassing MyDataAndImageClass, then it could very well fail in its `init`. – jbrennan Aug 17 '09 at 13:40
  • I agree with both Chuck and jbrennan. Plus, when I make a new class it takes 5 seconds to write `if (self = [super init])`, but a considerable amount more to find out if my superclass can return nil. Which it can, of course — when you run out of memory. – iKenndac Aug 17 '09 at 13:48
  • 1
    but in my subclass I wouldn't be calling -init, I'd be calling initWithImage: or initWithContentsOfFile: in which case, I would expect a nil. But when subclassing an NSObject, should nil ever come around? – Jasarien Aug 17 '09 at 14:02
  • 1
    Yes — if you run out of memory. It's rare, but it can happen. Plus, for the sake of a line of code, why are you so hell bent on not doing The Best Thing? Defensive code is good code. – iKenndac Aug 17 '09 at 14:05
  • 7
    I doubt NSObject init can return nil in any case, ever. If you're out of memory, the alloc will fail, but if it succeeds, I doubt init could fail - NSObject doesn't even have any instance variables except Class. In GNUStep, its implemented as just "return self", and disassembling on the Mac looks the same. All this is, of course, irrelevent - just follow the standard idiom and you wont have to worry about whether it can or can't. – Peter N Lewis Aug 17 '09 at 14:38
  • 10
    I'm not hell bent on not following best practices. I would, however, like to know why they are best practises in the first place. Its like being told to jump off a tower. You don't just go ahead and do it unless you know why. Is there a big, soft pillow to land on at the bottom, with a massive cash reward? If I knew that I'd jump. If not, I wouldn't. I don't want to just blindly follow a practise without know why I am following it... – Jasarien Aug 17 '09 at 15:12
  • That's also not to say that my code doesn't follow this. I do the check in all my subclasses, I just want to know why I'm doing it ;) – Jasarien Aug 17 '09 at 15:12
  • @Jasarien: The reason is that there's generally no guarantee any arbitrary init method will not return nil at some point, so it's a good practice to be into. Personally, I think it's kind of paranoid, but that is the reason. – Chuck Aug 17 '09 at 16:45
  • 4
    If alloc returns nil, init is sent to nil, which will always result in nil, this ending up with self being nil. – T . Aug 17 '09 at 18:32
  • 4
    @Chuck, what I don't get about all of this is that, when you construct an instance of a class, unless you KNOW that it might be null for some reason, you don't check if it's null. So your code will blow up happily anyway. So do these bulletproof constructors make sense in the context of code that doesn't check if the object is null anyway? Seems like a silly convention, and conciseness does count. – Dan Rosenstark Jul 09 '10 at 02:01
  • 1
    Another point that I don't see mentioned here is that you should check for `nil` being returned from `[super init]` because the superclass could change in the future. Just doing this simple check could save you some time down the line in this case. In addition, I believe Xcode now includes this check in the template code you get when you create a new Objective-C subclass so there's no longer any real reason to not have it there in your inits. – Beltalowda May 30 '12 at 16:21
  • Is this a concern in Swift, as well? – Ky - May 20 '16 at 15:58
50

This particular idiom is standard because it works in all cases.

While uncommon, there will be cases where...

[super init];

... returns a different instance, thus requiring the assignment to self.

And there will be cases where it will return nil, thus requiring the nil check so that your code doesn't try to initialize an instance variable slot that no longer exists.

The bottom line is that it is the documented correct pattern to use and, if you aren't using it, you are doing it wrong.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • 3
    Is this still true in light of the nullability specifiers? If my superclass's initializer is non-null then is it really still worth the extra clutter to check? (Although NSObject itself doesn't seem to have any for its `-init` afaict…) – natevw Dec 17 '15 at 18:14
  • Is there ever a case where `[super init]` returns nil when the direct superclass is `NSObject`? Isn't this a case where "everything is broken?" – Dan Rosenstark Jul 09 '20 at 00:02
  • 1
    @DanRosenstark Not if `NSObject` is the direct superclass. But... even if you declare `NSObject` as the direct superclass, something could have been modified at runtime such that `NSObject`'s implementation of `init` is not what is actually called. – bbum Jul 09 '20 at 05:04
  • 1
    Thanks so much @bbum, this really helped me orient in some bug fixing. Good to rule some things out! – Dan Rosenstark Jul 15 '20 at 21:05
27

I think, in most classes, if the return value from [super init] is nil and you check it, as recommended by standard practices, and then return prematurely if nil, basically your app is still not going to work correctly. If you think about it, even though that if (self != nil) check is there, for proper operation of your class, 99.99% of the time you actually do need self to be non-nil. Now, suppose, for whatever reason, [super init] did return nil, basically your check against nil is basically passing the buck up to the caller of your class, where it would likely fail anyways, since it will naturally assume that the call was successful.

Basically, what I'm getting at is that 99.99% of the time, the if (self != nil) does not buy you anything in terms of greater robustness, since you're just passing the buck up to your invoker. To really be able to handle this robustly, you would actually need to put in checks in your entire calling hierarchy. And even then, the only thing it would buy you is that your app would fail a little more cleanly/robustly. But it would still fail.

If a library class arbitrarily decided to return nil as a result of a [super init], you're pretty much f***ed anyways, and that's more of an indication that the writer of the library class made a mistake of implementation.

I think this is more of a legacy coding suggestion, when apps ran in much more limited memory.

But for C level code, I would still typically check the return value of malloc() against a NULL pointer. Whereas, for Objective-C, until I find evidence to the contrary, I think I'll generally skip the if (self != nil) checks. Why the discrepancy ?

Because, at the C and malloc levels, in some cases you actually can partially recover. Whereas I think in Objective-C, in 99.99% of cases, if [super init] does return nil, you're basically f***ed, even if you try to handle it. You might as well just let the app crash and deal with the aftermath.

Gino
  • 1,593
  • 17
  • 22
  • 6
    Well spoken. I second that. – Roger C S Wernersson Dec 11 '12 at 08:17
  • 3
    +1 I totally agree. Just a minor note: I don't believe that the pattern is a result from times where allocation more often failed. Allocation is usually already done at the time init is called. If alloc failed, init wouldn't even be called. – Nikolai Ruhe Dec 15 '13 at 16:21
8

This is kind of a summary of the comments above.

Let's say the superclass returns nil. What's gonna happen?

If you don't follow the conventions

Your code is gonna crash in the middle of your init method. (unless init does nothing of significance)

If you follow the conventions, not knowing that the superclass might return nil (most people end up here)

Your code is probalby gonna crash at some point later, because your instance is nil, where you expected something different. Or your program is gonna behave unexpectedly without crashing. Oh dear! Do you want this? I don't know...

If you follow the conventions, willingly allowing your subclass to return nil

Your code documentation(!) should clearly state: "returns ... or nil", and the rest of your code needs to be prepared for handling this. Now it makes sense.

Community
  • 1
  • 1
user123444555621
  • 148,182
  • 27
  • 114
  • 126
  • 5
    The interesting point here, I think, is that option #1 is *clearly preferable* to option #2. If there are genuinely circumstances in which you might want your subclass's init to return nil, then #3 is to be preferred. If the only reason that would ever happen is because of a bug in your code, then use #1. Using option #2 is just delaying your app exploding until a later point in time, and thereby making your job when you come to debug the error that much harder. It's like silently catching exceptions and continuing without handling them. – Mark Amery Jul 05 '13 at 15:20
  • Or you switch to swift and just use optionals – AndrewSB May 14 '15 at 22:59
7

Typically, if your class derives directly from NSObject, you won't need to. However, it's a good habit to get into, as if your class derives from other classes, their initializers may return nil, and if so, your initializer can then capture that and behave correctly.

And yes, for the record, I follow the best practice and write it on all my classes, even those deriving directly from NSObject.

John Rudy
  • 37,282
  • 14
  • 64
  • 100
  • 1
    With this in mind, would it be good practice to check for nil after initializing a variable and before calling functions on it? e.g. `Foo *bar = [[Foo alloc] init]; if (bar) {[bar doStuff];}` – schmittsfn Sep 04 '13 at 19:14
  • Inheriting from `NSObject` does not guarantee its `-init` gives you `NSObject` too, if counting exotic runtimes like older versions of GNUstep in (which returns `GSObject`) so no matter what, a check and an assign. – Maxthon Chan Oct 23 '13 at 09:27
3

A common mistake is to write

self = [[super alloc] init];

which returns an instance of the superclass, which is NOT what you want in a subclass constructor/init. You get back an object that does not respond to the subclass methods, which can be confusing, and generate confusing errors about not reponding to methods or identifiers not found, etc.

self = [super init]; 

is needed if the super class has members (variables or other objects) to initialize first before setting up the subclasses' members. Otherwise the objc runtime initializes them all to 0 or to nil. (unlike ANSI C, which often allocates chunks of memory without clearing them at all)

And yes, base class initialization can fail because of out-of-memory errors, missing components, resource acquisition failures, etc. so a check for nil is wise, and takes less than a few milliseconds.

Chris Reid
  • 460
  • 4
  • 9
3

You're right, you could often just write [super init], but that wouldn't work for a subclass of just anything. People prefer to just memorize one standard line of code and use it all the time, even when it's only sometimes necessary, and thus we get the standard if (self = [super init]), which takes both the possibility of nil being returned and the possibility of an object other than self being returned into account.

andyvn22
  • 14,696
  • 1
  • 52
  • 74
2

This is to check that the intialazation worked, the if statement returns true if the init method did not return nil, so its a way to check creation of the object worked correctly. Few reasons i can think of that init might fail maybe its an overriden init method that the super class does not know of or something of the sort, I wouldnt think it is that common though. But if it does happen, its better nothing to happen that a crash i supose so its always checked...

Daniel
  • 22,363
  • 9
  • 64
  • 71
  • it is, but htey are called together, what happens if alloc f ails? – Daniel Aug 17 '09 at 13:50
  • I imagine if alloc fails, then init will be sent to nil rather than an instance of whatever class you're calling init on. In that case, nothing will happen, and no code will be executed to even test whether or not [super init] returned nil. – Jasarien Aug 17 '09 at 15:08
  • 2
    Memory allocation is not always done in +alloc. Consider the case of class clusters; an NSString doesn't know which specific subclass to use until the initializer is invoked. – bbum Aug 17 '09 at 18:36
1

In OS X, it's not as likely for -[NSObject init] to fail due to memory reasons. The same cannot be said for iOS.

Also, it's good practice for writing when subclassing a class that might return nil for whatever reason.

MaddTheSane
  • 2,981
  • 24
  • 27