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

Discussion in 'Mac Programming' started by MacDonaldsd, Jul 28, 2008.

  1. MacDonaldsd macrumors 65816

    MacDonaldsd

    Joined:
    Sep 8, 2005
    Location:
    London , UK
    #1
    Hi,

    I have just been looking through some of the iPhone SDK examples and I have seen

    if (self = [super init])

    within the init method

    Before hand I have just been using

    [super init];

    Have I been doing it wrong, whats the reason/benefits of the two ways ?
     
  2. MrFusion macrumors 6502a

    Joined:
    Jun 8, 2005
    Location:
    West-Europe
    #2
    I think you should always use
    if (self = [super init])

    An init method will always return an object, which has to go somewhere.
    Calling only [super init], might run all the required steps, but self will not point to this object.
     
  3. MacDonaldsd thread starter macrumors 65816

    MacDonaldsd

    Joined:
    Sep 8, 2005
    Location:
    London , UK
  4. Nutter macrumors 6502

    Joined:
    Mar 31, 2005
    Location:
    London, England
    #4
    The above post is inaccurate — by the time your -init method is called, self already points to the object returned from +alloc.

    The (self = [super init]) idiom is often recommended because there's theoretically a possiblity that [super init] may return a different object altogether. However, in practice, [super init] on its own is absolutely fine. No (public) class in Cocoa returns a new object from -init, and if one did, you'd really have to know that fact before you could properly subclass.
     
  5. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #5
    The object that [super init] returns might not be the same as "self". It might for reasons only known to it dealloc your self object and create a new one and return that. Or it might just dealloc your self object and return nil. Some classes do that.

    For example: NSNumber has a few pre-made objects representing the numbers 0 to 11. So if you call [[NSNumber alloc] initWithInteger: 5] (no idea if initWithInteger is the right name), initWithInteger might dealloc what you passed in and return the pre-made object with a value of 5 with an additional retain count to safe memory.
     
  6. Nutter macrumors 6502

    Joined:
    Mar 31, 2005
    Location:
    London, England
    #6
    That's not how Apple implements class clusters.

    +[NSNumber allocWithZone:] returns a private subclass of NSNumber (NSPlaceholderNumber, I believe) which overrides the various -init... methods to return an appropriate concrete subclass.

    More detail here.

    Only the placeholder's -init... methods return new objects, and you're not likely to be subclassing the placeholder.

    EDIT: I know this is an implementation detail, and it's never good to make assumptions about implementation. But:
    1) I've never had occasion to subclass a class cluster.
    2) I can't think of a case when a call to -[super init] that returns a new object would be fixed by (self = [super init]). Take the above example: if [NSNumber initWithInteger:5] released the receiver and returned a shared instance, then surely the returned instance would be a concrete subclass of NSNumber, not your custom subclass. This would screw things up in a really rather big way.
     
  7. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #7
    Some objects return nil for init. For example NSPipe, NSSpeechRecognizer. However, I'm not sure what would happen if you subclassed these and [self init] returned nil (would your class be deallocated?).
     
  8. Nutter macrumors 6502

    Joined:
    Mar 31, 2005
    Location:
    London, England
    #8
    Oh yes, good point kainjow — you must always check for a nil return value, which indicates an error.

    My designated initialisers always look like this:

    Code:
    if ([super init] == nil)
    	return nil;
    
    ...
    
    return self;
    
    If -[super init] returns nil, it's probably because the object has released itself, so attempting to proceed will result in memory stomping.

    I'm not aware of NSPipe or NSSpeechRecognizer doing this for any special reason. Many classes do this if they encounter an error.
     
  9. MrFusion macrumors 6502a

    Joined:
    Jun 8, 2005
    Location:
    West-Europe
    #9
    Ok. Thanks for the correction.
     
  10. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #10
    In both classes, [self init] will return nil if the init call failed. When that happens, the object is already deallocated. If you just call [super init] without storing the result in "self", the value of "self" is unchanged, but the object doesn't exist anymore so using "self" from that point on will be leading to trouble. Returning "self" would cause even more trouble because the caller wouldn't have any clue that the subclass init has failed. If you write

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

    you avoid all these problems.
     
  11. Krevnik macrumors 68040

    Krevnik

    Joined:
    Sep 8, 2003
    #11
    While I agree that really the only reason you need to check the result of a superclass' init is to check for null... I much prefer the standard code style posted in other posts. It is a little clearer what is going to happen, IMO.
     
  12. Nutter macrumors 6502

    Joined:
    Mar 31, 2005
    Location:
    London, England
    #12
    Why is this any better than if ([super init]) { ... }?

    What could possibly be clearer than if ([super init] == nil) return nil;?
     
  13. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #13
    Old, but still pretty good, Will Shipley blog post on all this...
     
  14. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #14


    Because first it sets self to nil if [super init] returns nil, so you can just return self at the end of the function. And because second [super init] may return something different from self - [super init] could delete the object self and return a different object.
     
  15. Krevnik macrumors 68040

    Krevnik

    Joined:
    Sep 8, 2003
    #15
    There is a saying on my team when it comes to C# programming: Better to create something that will fail quickly and immediately if you do something horribly wrong (and subtle), then mask it. By setting self to the result of the super's init, if for some freaky reason something is going wrong, you will know about it quickly. That is, unless you run into the scenario like in Will's blog post. In which case, your version isn't any safer.

    A big part of it is coding style for the user/team. Again, on my team it is usually considered bad form to be returning from a method in too many places. Especially when it can be shown as:

    Code:
    - (id)init {
      self = [super init];
      if( self != nil ) {
        // Do my quick initialization here
      }
      return self;
    }
    
    The init code should be very short and concise in general, so the issues of reading through a long if shouldn't apply here. If they do, you really need to refactor the code and move some of the initialization code out to other methods for readability.

    Also, it is a bit easier to abort your initialization if needed, in my opinion.

    Of course, not everyone agrees that the same code pattern is the 'best', but the rules really only are to pick one, stick with it, and make sure everyone on your team agrees to it. Considering they tend to generate similar code, it is more about readability for those involved, rather than any particular syntactic superiority.
     
  16. teguh123 macrumors member

    Joined:
    Mar 22, 2011
    #16
    Let me rephrase that then.

    Most of the time, self points to alloc and never change.

    Once in a while, [super init] points to another memory.

    Damn... So why the hell we need both alloc and init? Why not have new?

    Like C++

    That leads to another problem.

    As we know, the child is always bigger than the base.

    Say [super init] returns a pointer to 100 bytes chunk of memory.

    Now self will point to that place, but this self requires 200 bytes of memory.

    How would the technical work out?
     
  17. jiminaus macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #17
    C++ and Objective-C are the same. operator new = alloc, constructor = init.
     
  18. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #18
    By the time any init method is being called, alloc has already allocated the correct sized piece of memory for the object. Initialization is not the same thing as allocation.
     
  19. GorillaPaws macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #19
    You can use new (it's simply calling an alloc and init for you--I'm pretty sure).

    Alloc/init is such a common pattern because it's very typical to write a custom init method that sets up some initial conditions that for consistency's sake all objects use the alloc/init pattern.

    This is an excellent article on Cocoa Initializers by Mike Ash: "The How and Why of Cocoa Initializers".
     
  20. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #20
    Style. First, why do you need verbose clarity? Once the basic concept of -init is understood, the template becomes so consistent that you really no longer struggle to understand that part of the code. But the other style point (personal taste, perhaps) is that any given function should have one and only one return statement. Multiple return statements in one function make code more difficult to read, and look just plain inelegant.
     
  21. teguh123, Apr 9, 2011
    Last edited: Apr 9, 2011

    teguh123 macrumors member

    Joined:
    Mar 22, 2011
    #21
    Also you need to assign self to the [super init] I think. Your code doesn't.

    My idea is the following

    Say class Kid inherit from class Parent. Size of parent is 100 bytes and size of kid is 200 bytes.

    In Kid init, we have self=[super init]

    Most of the time it's not a problem. However, once in a while the parent class dealloc the memory allocated for Kid and reallocate another memory for Parent and then give the pointer of the Parent to self

    So now, self points to a memory allocated by [super init], which is 100 bytes. But Kid needs 200 bytes

    Let me guess, does [super init] return Parent or a Kid class?

    If [super init] return Kid class, how do super knows that it's being called by the Kid class?
     
  22. Krevnik macrumors 68040

    Krevnik

    Joined:
    Sep 8, 2003
    #22
    I can always [self class] to get the meta-class of the object. Even if called in a parent class, 'self' always points to the real object. I can do something to the effect of:

    Code:
    id newInstance = class_createInstance([self class], 0);
    self = newInstance;
    
    This will create a new instance of the class 'self' is, and then assign self to be the new instance. This will work even in parent classes.

    That said, this sort of reallocation is rare. Cocoa more commonly uses this to return constant objects and the like for NSNumber and so on. In which case, you shouldn't be messing with the contents, and 'self' is no longer the same type as your class for good reason. A more common situation is that '[super init]' returns nil, and you want to also return nil. The syntax can either be:

    Code:
    if([super init] == nil) return nil;
    /* do initialization */
    
    or

    Code:
    self = [super init];
    if( self ) {
      /* do initialization */
    }
    return self;
    
    The latter has the advantage that it is more flexible, if not more readable. If the super class re-allocs self, or returns nil, you do the right thing in both cases. The first one is clear about what situation it handles, but a re-alloc'd object will fail.
     
  23. chown33, Apr 9, 2011
    Last edited: Apr 9, 2011

    chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #23
    This is completely wrong.

    No normal init method allocates memory for the new object (self in -init). The +alloc class-method is where memory is allocated for a new object.

    If [[Kid alloc] init] is written, then Kid's init receives a self object with 200 bytes.
    If [[Parent alloc] init is written, then Parent's init receives self of 100 bytes.

    When Kid's init calls [super init], the Parent init method initializes the 100 bytes that are common with Parent objects. Since Parent has no knowledge of any other bytes, it can't initialize them. When [super init] returns, the remainder of Kid's init should initialize the bytes that are unique to Kid objects. It can also override the initialization of some or all that [super init] performed.

    Developer-extensible classes whose -init doesn't return the original self are uncommon, and require advanced knowledge to write properly. When they exist, their documentation describes how they should be sub-classed.


    You really need to review the fundamentals of Objective-C and Cocoa:
    Cocoa Fundamentals Guide

    If you're not learning from a book or tutorial, I recommend that you do so. If you're already doing this, post exactly which book or tutorial you're using: title, author, url.
     
  24. Sydde, Apr 9, 2011
    Last edited: Apr 9, 2011

    Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #24
    A properly written class could handle this issue very easily. super does not affect the value of self, only the instance method being used. The self context frame contains a variable called "isa", which identifies the class of the current instance. Calling super does not change isa. Hence, if the superclass really did have a need to change the instance (e.g., old school memory zone manipulation), it could (and probably should) be done transparently to any possible subclass waiting on the init:
    Code:
    - (id)init {
         BOOL  shouldRealloc;
         id       alternate;
         
         if ( self = [super init] ) {
              // some code that determines how to handle this instance
              if ( shouldRealloc ) {
                   alternate = [[self class] alloc];
                   [self release];
                   self = alternate; // switches instance context
                   self = [super init];
              }
              // double check this object
              if ( self != nil ) {
                   // do regular initialization
              }
         }
         return self;
    }
    Note that +alloc returns a clean, properly sized object for a given class, with isa set to the correct class, ready for initialization. Since this is what you should expect from [super init], you place it at the beginning to avoid the remote risk of any initialization you have done being wiped by the superclass.
     

Share This Page