Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What to do if [super init] returns nil?

Tags:

objective-c

Take the following code as an example

- (id)init {
    self = [super init];
    if (self) {
        // code
    }
    return self;
}

I do not want nil to propagate up the calling hierarchy. My initial idea is to throw an exception in case self is nil, make a restore point and abort execution.

Better ideas?

like image 340
Pétur Ingi Egilsson Avatar asked Sep 28 '13 19:09

Pétur Ingi Egilsson


4 Answers

NSObject's implementation of [super init] will never return nil. The base implementation just returns self.

In general, the only reason that an initializer returns nil is if a nonfatal error occurred. For example, you might have called -initWithContentsOfURL:error: and passed an invalid URL. By convention, methods that may fail in this way have an error: parameter, which contains information about the failure. Most initializers do not have the possibility of a recoverable error, so like NSObject, they will never return nil.

Fatal errors typically throw an exception or abort the program. So checking for nil is no help with them. Your best bet to handle fatal errors is NSSetUncaughtExceptionHandler, although you should be aware that saving data is risky in the case of a fatal error, as the unsaved data may be corrupted. Don't overwrite good data in that case.

Why does objective-c code always check for nil in initializers, even when super will never return nil? Convention, mostly. Arguably, by always checking for nil, it becomes easier for a superclass to add a failure condition in the future without requiring subclasses to be changed, but really it's just a convention.

Finally, the initalizer is not the right place to check for failure in a superclass initializer. If recoverable errors are a possibility, the caller should check for the error.

Example:

NSError *error;
FooClass *myFoo = [[FooClass alloc] initWithContentsOfURL:blah error:&error]
if (myFoo == nil) {
  // ...
} else {
  // ...
}

Checking for nil whenever you initialize an object is overkill. This only needs to be done when there is an error: argument, or the method has a documented recoverable error.

like image 117
Chris Devereux Avatar answered Nov 12 '22 14:11

Chris Devereux


From the docs:-

For other sorts of errors, including expected runtime errors, return nil, NO, NULL, or some other type-‐suitable form of zero to the caller. Examples of these errors include the inability to read or write a file, a failure to initialize an object, the inability to establish a network connection, or a failure to locate an object in a collection. Use an NSError object if you feel it necessary to return supplemental information about the error to the sender. An NSError object encapsulates information about an error, including an error code (which can be specific to the Mach, POSIX, or OSStatus domains) and a dictionary of program-‐specific information. The negative value that is directly returned (nil, NO, and so on) should be the principal indicator of error; if you do communicate more specific error information, return an NSError object indirectly in a parameter of the method.

like image 44
Rahul Tripathi Avatar answered Nov 12 '22 15:11

Rahul Tripathi


Generally speaking, just don't care and let the nil propagate.

If [super init] is returning nil (i.e. a new object could not be instantiated), something is messed up so badly that your application is likely going to crash in a matter of moments anyway.

Checking for nil after every instantiation as suggested by Michael is cumbersome and probably totally useless for the reasons above.

If there's a specific class that you are worried about, and you really want to bail out as quick as possible, go ahead as you planned and throw an exception.

About "making a restore point" you can try to save whatever possible, but the scenario is so compromised that there's no guarantee of succeeding.

like image 4
Gabriele Petronella Avatar answered Nov 12 '22 15:11

Gabriele Petronella


I suppose "[super init]" could throw nil if you're trying to reserve a 20 gig block of memory or something (which nobody doing iOS coding would ever do), but in general, a "nil" being returned rarely happens in production code.

The nice thing about "nil" being returned is that you can send messages to a nil object and your application won't crash.

But you should always do checks for nil after instantiating an object, just to make sure you don't get too deep into something your app (or your user) can't recover from.

In Apple's "Concepts in Objective C" document, they suggest "When you create an object, you should generally check whether the returned value is nil before proceeding:".

like image 2
Michael Dautermann Avatar answered Nov 12 '22 14:11

Michael Dautermann