3

I stumbled upon this post In Objective-C why should I check if self = [super init] is not nil?

I can understand this syntax:

- (id)initWithString:(NSString *)aString
{
    self = [super init];
    if (self)
    {
        instanceString = [aString retain];
    }
    return self;
}

or this syntax:

- (id)init;
{
 if (!(self = [super init]))
   return nil;

 // other stuff
 return self;
}

but I still don't understand the "standard" template syntax

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

Can someone tell as clearly as possible what (3) does more or less compared to (1) or (2) ? All I read is so confusing (why people can't agree with something which is purely technical seems like politics :))

Nevertheless as I read authors article, and as I can fuzzily understand it goes far beyond just syntactic sugar debate or matter of taste. For example it is said:

Curiously then, while case 3 is overwhelmingly more common, initializers that support 1, 2 and 4 but are incompatible with case 3 have become the standard.Curiously then, while case 3 is overwhelmingly more common, initializers that support 1, 2 and 4 but are incompatible with case 3 have become the standard.

So I'd like to have a deep philosophical answer from Objective C Gurus if possible.

Community
  • 1
  • 1
user310291
  • 36,946
  • 82
  • 271
  • 487

4 Answers4

5

All of the three accomplish the exact same task: call [super init], assign it to self (in case super's init returns a different object, which in some cases is possible) and test whether that returned pointer is nil. If it is nil, leave without doing anything else.

Examples #1 and #3 are identical, they just have the syntax slightly rearranged. #2 is different in that it returns an explicit value of nil, while #1 and #3 do so implicitly.

Which on to use is a matter of taste. I prefer #2 because it saves an indention level (I like to avoid indention if possible, because if you have indented four or five levels deep it really gets hard to follow the flow).

People do not universally agree which one to use for the very same reason they cannot agree which indention style (K&R, Allman, GNU, Whitesmith, ...) to use: it's personal preference and does not change the logic.

DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • As I read authors article, its goes far beyond just syntactic sugar or matter of taste :) For example it is said here: "Curiously then, while case 3 is overwhelmingly more common, initializers that support 1, 2 and 4 but are incompatible with case 3 have become the standard.Curiously then, while case 3 is overwhelmingly more common, initializers that support 1, 2 and 4 but are incompatible with case 3 have become the standard." – user310291 Mar 04 '12 at 11:28
  • @user310291: The three variants you've cited all check for `nil`, but the paragraph from the article you've cited is about what `[super init]` can return. All of the three variants you've cited behave exactly the same here. To "fix" this, the author proposes a new variant, which is to store the return value of `[super init]` in a variable and compare that to the (still unchanged) `self`. So for the three variants you've cited, it really *is* a matter of taste and syntax. All of them behave exactly the same. – DarkDust Mar 04 '12 at 12:32
  • He contests that [super init] can return 4 cases and that syntax 3 only copes with 3 cases whereas it doesn't cope with 4th case which can be rather frequent. So it's not a matter of taste: it's a matter of taking into acconunt all exceptions otherwise your app is not robust don't you agree ? – user310291 Mar 04 '12 at 13:20
  • No. First, let's get the naming straight: I'll name your three code examples A, B and C. The article now defines four possible return values for `[super init]`, let's name them I, II, III and IV. Code example A can correctly cope with I, II and IV, but not with III. And the exact same thing is true for B and C as well. Matt complained that these schemes are the most common ones. He showed a different way, which can cope with III, but not with II. So whether you chose A, B or C is still a matter of taste, all three can cope with I, II and IV and neither can cope with III. – DarkDust Mar 04 '12 at 13:32
  • Why would one single way of doing init ? Because it is dangerous since if you are in some cases you didn't even notice and your code failed you don't even know why. – user310291 Mar 05 '12 at 20:13
  • You've understood that with all the proposed solutions there's always at least one case where it can fail, right? But you know about the super class and thus you know which cases can happen and which not. E.g. if you just derive from NSObject, you do not need to care about getting a different object back: you either get your original `self` back or `nil` (in this case even `nil` cannot happen; `alloc` would've failed instead). It's not necessary to handle all the return values that Matt talks about because for a given class it is not possible that all four can happen at runtime. Only a subset. – DarkDust Mar 05 '12 at 20:38
5

The key to understanding (3) is the if line

if((self = [super init])) {

In C, every operator has a return value, you just don't have to use it. So just as 3 + 4 returns 7, the operator = returns the same value that was just assigned. That allows you to do interesting things like this with it:

int a, b, c, d;

a = b = c = d = 5;

(This works because the operator has right-to-left associativity. This means that a = b = 3;is equivalent to a = (b = 3); so b is set to three first, then a is set to that same value.)

So now we can observe that the test

if((self = [super init])) {

is exactly the same as

self = [super init];
if (self) {

They all do exactly the same thing, though. As for the preferred option, a lot of people think that putting expressions into if statements is bad practice, as it's hard to notice if you're not used to it. Apple do seem to have settled with the first example you listed in their templates, and have also added a compiler warning for the first, if you try to do it without the stupid double brackets if(()).

joerick
  • 16,078
  • 4
  • 53
  • 57
  • As for your question regarding the Cocoa with Love article, I believe he is talking about the rare case where `[super init]` returns a singleton instance of the superclass, in this case he doesn't want to overwrite instance variables of the object. As far as I'm concerned, it's more of an academic curiousity rather than anything you need to worry about. – joerick Mar 04 '12 at 11:49
  • Thanks I better understand the syntax of 3 now so I give you the right answer since that 's what I asked in the first place. Nevertheless he contests that [super init] can return 4 cases and that syntax 3 only copes with 3 cases whereas it doesn't cope with 4th case which can be rather frequent. So it's not a matter of taste: it's a matter of taking into acconunt all exceptions otherwise your app is not robust don't you agree ? – user310291 Mar 04 '12 at 13:21
  • It's a pretty rare case in Cocoa that you'd want to subclass one of these objects that will cause problems. The examples are class cluster or singleton objects. I can think of no reason to subclass a singleton, at least, without making another singleton reference to it. And as for subclassing class clusters, generally in Cocoa we prefer [object composition](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/doc/uid/20000154-397934), rather than inheritance. – joerick Mar 04 '12 at 13:35
  • 1
    Just to be clear, these examples are all identical with respect to the problems that Matt Gallagher discusses. His conclusion is that there is no initialiser that will work perfectly in every case. – joerick Mar 04 '12 at 13:36
  • Yes but people should be aware of the case so as to adapt initializer to that case. It seems that people just uses standard template as myself until now automatically without really being concious. – user310291 Mar 04 '12 at 23:36
  • Apple now made a special exemption so when the line reads `if (self = [super init...])` no warning is emitted – Maxthon Chan Oct 23 '13 at 09:20
1

If you have an variable and no ==, !=, > or something like that the expression will be true if your variable (in this case a pointer to an object) is not 0. That means if your pointer don't point to nothing. nil and NULL are defined for 0 so you can write

if ((self = [super init]) != nil) {}

or

if ((self = [super init]) != NULL) {}

or

if ((self = [super init]) != 0) {}

or simply

if ((self = [super init])) {}

too.

qwertz
  • 14,614
  • 10
  • 34
  • 46
0

I also prefer the extra "room" afforded by NOT using the overly verbose, rarely intuitive... and frankly ugly syntax I see in Apple's code / around town, etc...

-(id) init {  if (self != super.init) return nil; ...

I may be drowning babies - and causing handsome old Indians to weep in the process - but I just stick it all on one line... and remember to return self; AOK so far!

Alex Gray
  • 16,007
  • 9
  • 96
  • 118