3

A usual initialization sequence in an Objective-C instance (e.g. in the designated initializer) goes like:

- (id)initWithFrame: (NSRect)frame {
  self = [super initWithFrame: frame];
  if (self != nil) {
    // Do your stuff.
  }
  return self;
}

This is a well known pattern, but is it really necessary to test if self is assigned? I mean, if something fails in the super method, wouldn't it rather raise an exception than just returning nil? Is it a safe pattern at all? What if the call to super had a problem but still returns a (random) pointer?

It's certainly a matter of defensive programming but it really looks exaggerating to have so many many tests for cases you know for sure that there's never a nil result. It makes of course a lot of sense to check self if you know that it can occur (which must be documented then).

Mike Lischke
  • 48,925
  • 16
  • 119
  • 181
  • 1
    Chances are pretty good that if you return `nil`, you'll still run into an exception, just not in the `init` method. – nhgrif Dec 15 '13 at 15:06
  • Yes. Follow the established convention. – uchuugaka Dec 15 '13 at 16:00
  • If the call to `super` had a problem and it then returns garbage instead of `nil` or a valid pointer, then there is a serious bug in that code and the developer should be flogged. – rmaddy Dec 15 '13 at 16:30
  • 1
    I just noticed that this is an old discussion: http://stackoverflow.com/q/1287950/104790 – Nikolai Ruhe Dec 15 '13 at 16:37

3 Answers3

3

Raising an exception is not reasonable behavior in an objective-c program when something goes wrong. Throwing exception should only used for critical, unrecoverable, programmer-error situations.

The correct way to signal error from an init method is to return nil and optionally fill in a passed NSError-pointer with more information. Testing if your super initializer returns nil prevents you from doing useless initialization, and helps avoid possible crashes like:

@implementation { 
  int _foo; // instance variable
}

- (id)initWithFrame: (NSRect)frame {
  self = [super initWithFrame: frame];
  if (self != nil) {
    _foo = 2;
  }
  return self;
}

This will crash if the super initializer returns nil, because _foo = 2 is short-hand for self->_foo = 2, which would dereference a nil pointer. (Only message sends return nil when self is nil; accessing an ivar directly doesn't make that guarantee.)

Jesse Rusak
  • 56,530
  • 12
  • 101
  • 102
  • One fact that speaks strongly for returning nil instead of raising an error is that Objective-C has this paradigm where you usually do not need to check for nil. Maybe the check in the initializer is the price for that? – Mike Lischke Dec 15 '13 at 15:59
  • I think this question is not about exception vs. returning nil. Both cases have valid reasons and many examples in Apple's frameworks. The point is: Both cases have to be documented and reasons explained. If there's a chance that `nil` is returned from a superclass you obviously can't omit the condition. – Nikolai Ruhe Dec 15 '13 at 16:06
2

Some classes, even Apple's own NS classes can return nil from an initializer, e.g. NSArray's and NSDictionary's initWithContentsOfFile: and initWithContentsOfURL: methods. Should those throw an exception instead? NO! They're easily detectable and easily recoverable cases. Throwing an exception would be nothing but a crutch for lazy programmers who can't be bothered to put in proper error-checking and a bother for those of us who do.

Kevin
  • 53,822
  • 15
  • 101
  • 132
  • In those cases returning nil makes of course sense, but that is not the point. It's about checking for nil if there can't be nil (which I expect are far far more than those that can return nil). – Mike Lischke Dec 16 '13 at 08:40
0

The question boils down to: Are there any (many) known classes that do return nil from an initializer?

I think there are few. Of course there are known cases (where the constructor often takes an NSError by reference) that have to return nil to point out an error.

But most other initializers either return the self argument or another instance of the class. If that's not the case it should be clearly expressed in the documentation, as code usually relies on object creation not failing.

So I think it's totally safe to omit the test for nil when you checked the superclass's behavior.

On the other hand the pattern is so omnipresent that co-developers might be confused about the omission and might point it out as an oversight. So if you plan to switch to a style without the condition it should at least be somehow communicated to your fellow developers.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • That's along the line I was thinking. There are so many checks for assignments for cases which never will return nil. There are better ways to burn CPU cycles. – Mike Lischke Dec 15 '13 at 15:56
  • 2
    I'm not sure I agree with your statement that only "a few" classes return nil. For example, the designated initializer for UIView (`initWithFrame:`, as in the OP's question) states it could return nil, so any UIView could potentially return nil from its init method. I think it's common enough that I don't think it's safe to assume the absence of such a warning is a guarantee that the initialize cannot fail. – Jesse Rusak Dec 15 '13 at 16:15
  • 1
    @JesseRusak Yet most code *outside* of init is not aware of the possibility that initWithFrame: can return nil an will inevitably run into the exception from addSubview: where it complains about the nil argument. In cases where no such immediate nil check follows the app usually still does not work as expected. So crashing inside of init might actually be a good thing and make debugging easier. – Nikolai Ruhe Dec 15 '13 at 16:27
  • @JesseRusak I just filed a documentation bug (radar 15671536) regarding initWithFrame: returning nil. – Nikolai Ruhe Dec 16 '13 at 21:07
  • @NikolaiRuhe OK. I don't think they're going to change that, since it's a general rule that any init method could return nil, so it's certainly true for initWithFrame:. – Jesse Rusak Dec 16 '13 at 21:11
  • @JesseRusak It's exactly that general rule that is questioned here. – Nikolai Ruhe Dec 16 '13 at 21:29