self = [super init] debate.

  • Ok. Myself, like most Cocoa engineers have learned to write
    initializers like so:

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

    if (self != nil)
    {
      .. do my init
    }

    return self;
    }

    So my question is:

      As i get ready to teach non-cocoa programmers in my company about
    cocoa programming, and having read Will Shipley's blog, Is the above
    the way to teach writing an initializer? I see Will, and a few others,
    say no [super init] will initialize self in its code there is no
    reason to assign self. Yet all code and docs I see have similar code.

    Arguments for and against? I know old debate.... ;-)

    Scott Andrew
  • One point of research (going through my books.)

    The Cocoa with Objective-C book shows not assigning self.. Arg.. So
    where do we get in the habit of assigning self? Is it bad code
    practice? I know i have done it for the past 4 years out of habit and
    learning from example code and other materials.

    Scott

    On Feb 10, 2008, at 9:18 AM, Scott Andrew wrote:

    > Ok. Myself, like most Cocoa engineers have learned to write
    > initializers like so:
    >
    > -(id) init
    > {
    > self = [super init];
    >
    > if (self != nil)
    > {
    > .. do my init
    > }
    >
    > return self;
    > }
    >
    > So my question is:
    >
    > As i get ready to teach non-cocoa programmers in my company about
    > cocoa programming, and having read Will Shipley's blog, Is the above
    > the way to teach writing an initializer? I see Will, and a few
    > others, say no [super init] will initialize self in its code there
    > is no reason to assign self. Yet all code and docs I see have
    > similar code.
    >
    > Arguments for and against? I know old debate.... ;-)
    >
    > Scott Andrew
  • I'm no expert, but here is my take.

    This argument is not able what is correct, but about style.  Shipley
    is saying you're wasting 7 characters with the self = [super init]
    assignment because [super init] does the assignment for you.  Shipley
    is correct that you don't need to do the assignment, but I don't
    believe doing the assignment is ever going to cause problems, its just
    extra.

    But what's more important, writing correct code or writing code that
    is clear and correct?  Just because you don't have to do something, if
    you do it does that make the code incorrect?  I think clear and
    correct is better and doing something extra doesn't make the code
    wrong if its more clear.

    Is it more clear to write this:

    [super init];
    if(self != nil)

    or

    self = [super init];
    if(self != nil)

    I think it is the second because its explicit.

    My guess is that the majority of Cocoa programmers go the assignment
    route because that's what they've been taught.  When you are teaching
    your class you should tell your students that they don't have to do
    the assignment, but that by convention most Cocoa programmers do.
    That way they are informed about how things actual work.

    Adhamh

    On Feb 10, 2008, at 11:35 AM, Scott Andrew wrote:

    > One point of research (going through my books.)
    >
    > The Cocoa with Objective-C book shows not assigning self.. Arg.. So
    > where do we get in the habit of assigning self? Is it bad code
    > practice? I know i have done it for the past 4 years out of habit
    > and learning from example code and other materials.
    >
    > Scott
    >
    > On Feb 10, 2008, at 9:18 AM, Scott Andrew wrote:
    >
    >> Ok. Myself, like most Cocoa engineers have learned to write
    >> initializers like so:
    >>
    >> -(id) init
    >> {
    >> self = [super init];
    >>
    >> if (self != nil)
    >> {
    >> .. do my init
    >> }
    >>
    >> return self;
    >> }
    >>
    >> So my question is:
    >>
    >> As i get ready to teach non-cocoa programmers in my company about
    >> cocoa programming, and having read Will Shipley's blog, Is the
    >> above the way to teach writing an initializer? I see Will, and a
    >> few others, say no [super init] will initialize self in its code
    >> there is no reason to assign self. Yet all code and docs I see have
    >> similar code.
    >>
    >> Arguments for and against? I know old debate.... ;-)
    >>
  • I don't think that's correct.  self is an implicit variable on the
    caller's stack.  After you call the function (err, send the message)
    init, your local copy of self will be the same.  So the snippet of code:

    [super init]
    if (self != nil)

    won't do what you are saying.  self will still be a non-nil value.
    Setting self to the return value of init is just a way of making sure
    you don't send any other messages to this object if the init fails.

    -john

    On Feb 10, 2008, at 10:41 AM, Adhamh Findlay wrote:

    > I'm no expert, but here is my take.
    >
    > This argument is not able what is correct, but about style.  Shipley
    > is saying you're wasting 7 characters with the self = [super init]
    > assignment because [super init] does the assignment for you.
    > Shipley is correct that you don't need to do the assignment, but I
    > don't believe doing the assignment is ever going to cause problems,
    > its just extra.
    >
    > But what's more important, writing correct code or writing code that
    > is clear and correct?  Just because you don't have to do something,
    > if you do it does that make the code incorrect?  I think clear and
    > correct is better and doing something extra doesn't make the code
    > wrong if its more clear.
    >
    > Is it more clear to write this:
    >
    > [super init];
    > if(self != nil)
    >
    > or
    >
    > self = [super init];
    > if(self != nil)
    >
    > I think it is the second because its explicit.
    >
    > My guess is that the majority of Cocoa programmers go the assignment
    > route because that's what they've been taught.  When you are
    > teaching your class you should tell your students that they don't
    > have to do the assignment, but that by convention most Cocoa
    > programmers do.  That way they are informed about how things actual
    > work.
    >
    > Adhamh
    >
    > On Feb 10, 2008, at 11:35 AM, Scott Andrew wrote:
    >
    >> One point of research (going through my books.)
    >>
    >> The Cocoa with Objective-C book shows not assigning self.. Arg.. So
    >> where do we get in the habit of assigning self? Is it bad code
    >> practice? I know i have done it for the past 4 years out of habit
    >> and learning from example code and other materials.
    >>
    >> Scott
    >>
    >> On Feb 10, 2008, at 9:18 AM, Scott Andrew wrote:
    >>
    >>> Ok. Myself, like most Cocoa engineers have learned to write
    >>> initializers like so:
    >>>
    >>> -(id) init
    >>> {
    >>> self = [super init];
    >>>
    >>> if (self != nil)
    >>> {
    >>> .. do my init
    >>> }
    >>>
    >>> return self;
    >>> }
    >>>
    >>> So my question is:
    >>>
    >>> As i get ready to teach non-cocoa programmers in my company about
    >>> cocoa programming, and having read Will Shipley's blog, Is the
    >>> above the way to teach writing an initializer? I see Will, and a
    >>> few others, say no [super init] will initialize self in its code
    >>> there is no reason to assign self. Yet all code and docs I see
    >>> have similar code.
    >>>
    >>> Arguments for and against? I know old debate.... ;-)
    >>>

  • On 10 feb 2008, at 19:41, Adhamh Findlay wrote:

    > This argument is not able what is correct, but about style.  Shipley
    > is saying you're wasting 7 characters with the self = [super init]
    > assignment because [super init] does the assignment for you.
    > Shipley is correct that you don't need to do the assignment, but I
    > don't believe doing the assignment is ever going to cause problems,
    > its just extra.

    I hesistate to enter this debate, but I think I remember a remark in
    one of the Cocoa-ObjC manuals that said something like: you cannot be
    sure that the call to super returns the same object than the one that
    is calling. In that case it is more than style what counts here.

    Suppose [super init] returns another object (possibly in a more
    specialized class and possibly for reasons of efficiency and/or
    performance) by allocating and initializing it. In this case self
    should reflect that by being assigned that object. Thus
    >
    > [super init];
    > if(self != nil)
    then will be the original uninitialized, not the initialized object.
    Whereas the following construct does incorporate the change:
    > self = [super init];
    > if(self != nil) ...
    > return self;

    The point is, the caller of [super init] cannot know (without doing
    some introspection) what is returned. And therefore should take the
    surest path.

    Please correct me if I am seeing this wrong.

    Hans van der Meer
  • On Feb 10, 2008, at 09:18, Scott Andrew wrote:

    > -(id) init
    > {
    > self = [super init];
    >
    > if (self != nil)
    > {
    > .. do my init
    > }
    >
    > return self;
    > }
    >
    > So my question is:
    >
    > As i get ready to teach non-cocoa programmers in my company about
    > cocoa programming, and having read Will Shipley's blog, Is the above
    > the way to teach writing an initializer? I see Will, and a few
    > others, say no [super init] will initialize self in its code there
    > is no reason to assign self. Yet all code and docs I see have
    > similar code.

    The short answer: do the assignment to 'self'; it's wrong to omit it.

    The long answer:

    You can't rely on the discussion in the comments section of Shipley's
    blog, because it's effectively all off-topic. (Well, half of it is
    just plain wrong, and the rest of it is stuck on an irrelevant
    example.) Shipley's argument is basically this: if X is a subclass of
    Y, and the [super init] in X's init returns something other than an
    instance of X (partially initialized by Y's init), then the subclass
    can't work -- where would the subclass's instance variables be, for
    example? -- therefore, the [super init] must in fact return the
    instance you sent the [super init] to.

    That discussion was using something like NSArray as an example, since
    "creating" a NSArray actually creates a private class instance. So you
    write:

    @interface ArraySubclass : NSArray {
      int myIvar;
    }
    ...

    - (id) init {
      self = [super init]; // assume this returns a private class instance
    ...
      myIvar = 5; // oops -- the private class doesn't have this
    }

    Shipley tried to conclude from this that the "self =" assignment was
    at fault. In fact, what the example actually shows is that you cannot
    usefully subclass NSArray (or any other class cluster like it). That's
    why there are pages of Cocoa documentation explaining what to do
    *instead of* mistakenly subclassing NSArray.

    Slightly more generally, it shows that there is a *condition* for a
    subclass to be legal: the result of its calling [super init] must be
    something that works as a partially initialized instance of the
    subclass. If you're not sure of that, don't try to subclass.

    The other flaw in Shipley's conclusion is that, even if X's [super
    init] returns an instance of X, it may not be the instance you sent
    the [super init] to, so you'd better remember the returned value.

    For example, it's not hard to imagine that the superclass Y's init
    might copy the instance to a different memory zone and return the
    copy. Y might need some knowledge of objective C internals to make
    something like that feasible, but Apple tells us to assume that a
    different instance might be returned, so it seems wise to believe them.
  • On 10 Feb '08, at 9:18 AM, Scott Andrew wrote:

    > I see Will, and a few others, say no [super init] will initialize
    > self in its code there is no reason to assign self.

    No, you do [sometimes] need to assign self. 'self' is just a local
    variable (actually a parameter, one declared for you by the compiler.)
    The superclass's init method can't affect its value for you. However,
    in some cases the superclass method can decide to return a different
    object, and in that case unless you reassign self, you won't get the
    object the superclass initialized.

    Why would an init method return a different object? Usually because
    it's part of a "class cluster" design pattern, where the public class
    is abstract, and behind the scenes some concrete subclasses are used.
    Most of the Cocoa collection classes work this way. When you call "-
    [[NSArray alloc] init] the result isn't an instance of NSArray, it's
    usually an NSCFArray, an internal class that manages the bridging to
    CoreFoundation's CFArray.

    Another reason is to take advantage of variable-sized objects. You
    could have an array-like class that stores its array in itself, past
    the end of its regular instance variables. To do that, you have to
    call NSAllocateObject passing in the number of extra bytes to reserve.
    But you don't know that size until your -initWithSize: is called,
    after you've been allocated. So you make your -initWithSize call [self
    release], then NSAllocateObject with extra bytes, and assign that to
    self. The result is that -initWithSize returns a different instance
    than it was called on.

    Any subclass that calls [super initWithSize] and doesn't reassign self
    to the result now has a 'self' variable that points to an obsolete
    dealloced object, and is now very likely to either crash immediately
    or (more likely) corrupt the heap and cause a later crash. Bad news.

    -init methods that return a new object are rare. In most cases you
    don't need to add "self=" in front of the "[super init]" call. But in
    those few cases the consequences are grim, so it's better to train
    yourself to do it every time. It's only five more characters to type,
    and it should only compile into one or two extra machine instructions.

    —Jens
  • > As i get ready to teach non-cocoa programmers in my company about
    > cocoa programming, and having read Will Shipley's blog, Is the
    > above the way to teach writing an initializer? I see Will, and a
    > few others, say no [super init] will initialize self in its code
    > there is no reason to assign self. Yet all code and docs I see have
    > similar code.

    Initialisers do not have to - and often don't - return self.  They
    are free to release self and return nil, or a different object, which
    can even be a different class (though it's considered rather poor
    form to return an object which isn't at least an instance or subclass
    of the class who's initialiser is doing this magic).

    It's important to recognise that, if init fails, you're supposed to
    call [self release] and return nil, because typical code is:

    > MyClass *woot = [[MyClass alloc] init];

    If you just return nil without releasing, you've just leaked
    yourself.  So, ultimately the test for nil from [super init] is very
    often necessary, and is a good coding style.  Good coding style in
    the sense that it will prevent your program from crashing.

    Now, since we're all here already, and you've gotten me started, you
    must endure all of it. :P  So, truth be told, if you have a hierarchy:

    A -> B
    A -> C

    A's initialiser, when called by say B, could hypothetically release
    self, allocate an object of class C, and return that.  If you want to
    be really defensive in your coding, you could write:

    > self = [super init];
    >
    > if ((nil != self) && [self isKindOfClass:[MyClass class]]) {
    > ...
    > }
    >
    > return self;

    I've never seen anyone actually write that, though.  Typically your
    initialiser will return either nil, or an object of the same lineage
    (or a further subclass thereof) of the original object.  When you
    start considering scenarios like {A, B, C} above, things can get
    pretty silly very quickly.  And generally when people start doing
    things like this, it's because they've decided that transparent class
    clusters would be really cool, and I always wonder if such dubious
    practices are wise anyway...  the official Apple line in such cases,
    anyway, is don't subclass class clusters, so if you take that as
    gospel this is a moot point.

    And as Wil points out (http://www.wilshipley.com/blog/2005/07/self-
    stupid-init.html) if your superclass' initialiser is funky because it
    returns singletons (or similar) - which is the most common case of
    this sort of thing - then you have issues with re-initialising
    existing instances (which if nothing else will leak, because
    virtually no-one releases/frees existing ivars in their
    initialisers).  So you'd have to then write:

    > id newSelf = [super init];
    >
    > if (self == newSelf) {
    > ...
    > } else {
    > [self release];
    > self = newSelf;
    > }
    >
    > return self;

    However, that's assuming super always returns either self or an
    existing, fully-initialised instance, which as noted previously
    doesn't have to be the case...

    Generally, protecting against nil should be quite sufficient, as
    noted, and in the few cases where a class does do funky voodoo in its
    initialisers, it should document as such and explain exactly what it
    could hypothetically do, so you can write your subclasses appropriately.

    [[ pre-emptive retort:  some people argue that you shouldn't worry
    about initialisers returning nil because that means you're out of
    memory and your program is hosed anyway... this is complete crap -
    even aside from the fact that an initialiser returning nil could mean
    any of numerous things - and many programs are designed to - and will
    quite successfully - recover from such situations... iff they have
    proper checks in place and don't dereference NULL immediately ]]

    Wade
  • On Feb 10, 2008, at 1:29 PM, Quincey Morris wrote:

    >
    > On Feb 10, 2008, at 09:18, Scott Andrew wrote:
    >
    >> -(id) init
    >> {
    >> self = [super init];
    >>
    >> if (self != nil)
    >> {
    >> .. do my init
    >> }
    >>
    >> return self;
    >> }
    >>
    >> So my question is:
    >>
    >> As i get ready to teach non-cocoa programmers in my company about
    >> cocoa programming, and having read Will Shipley's blog, Is the
    >> above the way to teach writing an initializer? I see Will, and a
    >> few others, say no [super init] will initialize self in its code
    >> there is no reason to assign self. Yet all code and docs I see have
    >> similar code.
    >
    > The short answer: do the assignment to 'self'; it's wrong to omit it.
    >
    > The long answer:
    >
    > You can't rely on the discussion in the comments section of
    > Shipley's blog, because it's effectively all off-topic. (Well, half
    > of it is just plain wrong, and the rest of it is stuck on an
    > irrelevant example.) Shipley's argument is basically this: if X is a
    > subclass of Y, and the [super init] in X's init returns something
    > other than an instance of X (partially initialized by Y's init),
    > then the subclass can't work -- where would the subclass's instance
    > variables be, for example? -- therefore, the [super init] must in
    > fact return the instance you sent the [super init] to.
    >
    > That discussion was using something like NSArray as an example,
    > since "creating" a NSArray actually creates a private class
    > instance. So you write:
    >
    > @interface ArraySubclass : NSArray {
    > int myIvar;
    > }
    > ...
    >
    > - (id) init {
    > self = [super init]; // assume this returns a private class instance
    > ...
    > myIvar = 5; // oops -- the private class doesn't have this
    > }
    >
    > Shipley tried to conclude from this that the "self =" assignment was
    > at fault. In fact, what the example actually shows is that you
    > cannot usefully subclass NSArray (or any other class cluster like
    > it). That's why there are pages of Cocoa documentation explaining
    > what to do *instead of* mistakenly subclassing NSArray.
    >
    > Slightly more generally, it shows that there is a *condition* for a
    > subclass to be legal: the result of its calling [super init] must be
    > something that works as a partially initialized instance of the
    > subclass. If you're not sure of that, don't try to subclass.
    >
    > The other flaw in Shipley's conclusion is that, even if X's [super
    > init] returns an instance of X, it may not be the instance you sent
    > the [super init] to, so you'd better remember the returned value.
    >
    > For example, it's not hard to imagine that the superclass Y's init
    > might copy the instance to a different memory zone and return the
    > copy. Y might need some knowledge of objective C internals to make
    > something like that feasible, but Apple tells us to assume that a
    > different instance might be returned, so it seems wise to believe
    > them.

    I agree with what Quincey says here.  I know when I first started
    Cocoa, my inits were a bit different.  But, after reading many things
    (mostly from this list), I opted to keep all my inits in the following
    form:

    if ((self = [super init]) != nil)
        {
        [self setPropertyA:someValue];
        [self setPropertyB:...
        }

    return self;

    The way I look at this, it may be that this pattern is overkill for
    many situations, but I've yet to see a case where this pattern is the
    wrong thing to do.  And by wrong, I don't mean saving a few keystrokes
    in typing; wrong in that some compile and/or runtime issue will come
    up.  I'd rather spend my time writing business-logic code than
    attempting to figure out just what init pattern may save me on typing
    and/or a few compute cycles.

    Thus, I use this single pattern since I know it will work in all
    cases.  I also like more explicit code anyhow.  Even in Java, my
    constructors are usually like:

    int x;
    int y;

    public foo(int anX, int aY) {
        this.x = anX;
        this.y = aY;
    }

    It's not necessary to use 'this', yet I prefer to always do that to me
    more explicit.

    ___________________________________________________________
    Ricky A. Sharp        mailto:<rsharp...>
    Instant Interactive(tm)  http://www.instantinteractive.com
  • The real killer argument for reassigning 'self' is that the call to
    [super init] might fail, returning nil. If this happens, the
    superclass init method has released/dealloced the object (to avoid a
    leak) so your 'self' is now a dangling pointer to freed memory. In
    this case you must immediately return nil instead of doing any more
    setup.

    And -init methods do return nil for reasons other than out-of-memory.
    For example, -[NSString initWithUTF8String:] returns NIL if the
    parameter isn't valid UTF-8, and -[NSDictionary
    initWithContentsOfFile:] returns nil if the file isn't found or isn't
    a plist.

    So the following is buggy, and will crash or corrupt memory if the
    superclass's initializer ever fails:

    -(id) initWithFoo: (int)foo {
    [super initWithFoo: foo];
    _bar = 1;
    return self;
    }

    This is a real problem. I have run into actual bugs that were caused
    by writing an initializer this way.

    The following change is no help:

    -(id) initWithFoo: (int)foo {
    [super initWithFoo: foo];
    if( self )
      _bar = 1;
    return self;
    }

    The "if(self)" check is pointless because 'self' is a local variable;
    the superclass init method won't affect it. The only way to find out
    if the superclass init returned nil is to actually check its return
    value. The following would work:

    -(id) initWithFoo: (int)foo {
    if( [super initWithFoo: foo] )
      _bar = 1;
    return self;
    }

    If the superclass init fails, this will correctly avoid accessing
    'self', and instead return nil. What this doesn't support, however, is
    the previously-described case where the superclass initializer
    succeeds but returns a different object. To support that, you actually
    have to assign the return value of 'super init' to 'self'.

    -(id) initWithFoo: (int)foo {
    self = [super initWithFoo: foo];
    if( self )
      _bar = 1;
    return self;
    }

    It's possible to coalesce the first two lines into "if( self = [super
    initWithFoo: foo] )", but I avoid this because putting assignments
    inside 'if' or 'while' tests is a dangerous idiom ... it's very easy
    to type "==" instead of "=" and end up with a difficult-to-detect bug.

    —Jens
  • On 10 feb 2008, at 22:44, Jens Alfke wrote:

    > It's possible to coalesce the first two lines into "if( self =
    > [super initWithFoo: foo] )", but I avoid this because putting
    > assignments inside 'if' or 'while' tests is a dangerous idiom ...
    > it's very easy to type "==" instead of "=" and end up with a
    > difficult-to-detect bug.

    It is no problem if one takes care to phrase the if as:
        if (nil != (self = [super init])){...}
    I know, still more keystrokes, but programming safety is the more
    important consideration here and I force myself to not being lazy.

    By the way. Doing (nil !== ...) is a habit that saves one in comparing
    to variables as in (nil == myvariable). Compare this to (myvariable ==
    nil) that indeed is a bit dangerous.

    I hope this comment is not too far off topic for this list -- so I
    make this my last contribution on this subject.

    Hans van der Meer
  • On Feb 10, 2008, at 4:44 PM, Jens Alfke wrote:

    > The real killer argument for reassigning 'self' is that the call to
    > [super init] might fail, returning nil. If this happens,

    Has that ever happened?
  • On 10.02.2008, at 23:29, Paul Bruneau wrote:
    > On Feb 10, 2008, at 4:44 PM, Jens Alfke wrote:
    >> The real killer argument for reassigning 'self' is that the call to
    >> [super init] might fail, returning nil. If this happens,
    >
    > Has that ever happened?

      You might want to read past the first sentence, he actually provided
    several examples immediately thereafter.

    Cheers,
    -- Uli Kusterer
    "The Witnesses of TeachText are everywhere..."
    http://www.zathras.de
  • On Feb 10, 2008, at 5:40 PM, Uli Kusterer wrote:

    > On 10.02.2008, at 23:29, Paul Bruneau wrote:
    >> On Feb 10, 2008, at 4:44 PM, Jens Alfke wrote:
    >>> The real killer argument for reassigning 'self' is that the call
    >>> to [super init] might fail, returning nil. If this happens,
    >>
    >> Has that ever happened?
    >
    >
    > You might want to read past the first sentence, he actually
    > provided several examples immediately thereafter.

    Yes, you are right. I apologize.
  • Thank you everyone.. I was afraid to dig this back up again, I know it
    had been discussed alot. But this was a great conversation.

    Scott

    On Feb 10, 2008, at 3:18 PM, Paul Bruneau wrote:

    > On Feb 10, 2008, at 5:40 PM, Uli Kusterer wrote:
    >
    >> On 10.02.2008, at 23:29, Paul Bruneau wrote:
    >>> On Feb 10, 2008, at 4:44 PM, Jens Alfke wrote:
    >>>> The real killer argument for reassigning 'self' is that the call
    >>>> to [super init] might fail, returning nil. If this happens,
    >>>
    >>> Has that ever happened?
    >>
    >>
    >> You might want to read past the first sentence, he actually
    >> provided several examples immediately thereafter.
    >
    > Yes, you are right. I
    > apologize.
  • On Feb 10, 2008, at 13:44, Jens Alfke wrote:

    > The real killer argument for reassigning 'self' is that the call to
    > [super init] might fail, returning nil. If this happens, the
    > superclass init method has released/dealloced the object (to avoid a
    > leak) so your 'self' is now a dangling pointer to freed memory. In
    > this case you must immediately return nil instead of doing any more
    > setup.
    >
    > And -init methods do return nil for reasons other than out-of-
    > memory. For example, -[NSString initWithUTF8String:] returns NIL if
    > the parameter isn't valid UTF-8, and -[NSDictionary
    > initWithContentsOfFile:] returns nil if the file isn't found or
    > isn't a plist.
    >

    Just to follow up on a couple of minor points ...

    -- IIRC, in the discussion on Shipley's blog, his preferred coding
    pattern was:

    if (![super init])
      return nil;

    which does at least handle the nil-return case correctly. It
    mishandles the returns-a-different-instance case, as you noted before.

    -- Someone did an automated check of every Cocoa class in Tiger (maybe
    it was every subclass of NSObject, I don't remember exactly now) and
    counted the number of classes for which the receiver of [super init]
    was different from the returned value. The answer was 0.

    That may comfort anyone who wants to leave out the 'self =', but it's
    hardly a reliable argument. The number of dead people reading this
    discussion is also 0 (afaik), but I wouldn't take that as proof of
    immortality.

    -- Some classes cannot be subclassed -- cluster classes like NSArray,
    for example. These classes prove nothing one way or another about
    writing subclass initializers.

    Classes that return a singleton -- such as NSNull -- can't be
    subclassed either, so they're also ineffective examples.
  • On Feb 10, 2008, at 5:32 PM, Quincey Morris wrote:

    > -- Someone did an automated check of every Cocoa class in Tiger
    > (maybe it was every subclass of NSObject, I don't remember exactly
    > now) and counted the number of classes for which the receiver of
    > [super init] was different from the returned value. The answer was 0.
    >
    > That may comfort anyone who wants to leave out the 'self =', but
    > it's hardly a reliable argument. The number of dead people reading
    > this discussion is also 0 (afaik), but I wouldn't take that as proof
    > of immortality.
    >
    <http://developer.apple.com/documentation/Cocoa/Reference/CoreDataFramework/
    Classes/NSManagedObject_Class/Reference/NSManagedObject.html#//apple_ref/oc
    c/instm/NSManagedObject/initWithEntity:insertIntoManagedObjectContext:
    >

    mmalc
  • On 10 Feb '08, at 5:32 PM, Quincey Morris wrote:

    > -- Someone did an automated check of every Cocoa class in Tiger
    > (maybe it was every subclass of NSObject, I don't remember exactly
    > now) and counted the number of classes for which the receiver of
    > [super init] was different from the returned value. The answer was 0.

    IIRC, one or more of the NSURLxxx base classes does this — it might be
    NSURLProtocol — but I no longer have access to the source code so I
    can't look it up...

    It would be hard to do an automated check for this, because so many
    classes have initializers other than plain -init.

    —Jens
  • On Feb 10, 2008, at 5:32 PM, Quincey Morris wrote:

    > -- Someone did an automated check of every Cocoa class in Tiger
    > (maybe it was every subclass of NSObject, I don't remember exactly
    > now) and counted the number of classes for which the receiver of
    > [super init] was different from the returned value. The answer was 0.
    >
    > That may comfort anyone who wants to leave out the 'self =', but
    > it's hardly a reliable argument. The number of dead people reading
    > this discussion is also 0 (afaik), but I wouldn't take that as proof
    > of immortality.

    It would be impossible to test every conceivable set of arguments.
    For example, NSData may return instances of different classes
    depending on how large a chunk of memory is desired or if it is a
    memory mapped vs. malloc()d chunk o' bits.

    Similarly, this is entirely an implementation detail.  Any given class
    may return a subclass at some future time when it doesn't now.  Or it
    might even return a different class on a different architecture -- 32
    bit vs. 64 bit, for example.

    > -- Some classes cannot be subclassed -- cluster classes like
    > NSArray, for example. These classes prove nothing one way or another
    > about writing subclass initializers.

    Class clusters can be subclassed just fine and, while not terribly
    common, there are some very good reasons for doing so.

    It isn't hard, either, once you grok the pattern.  You are required to
    implement the methods that are core to the class.  All other methods
    are implemented on the base of the class cluster in terms of said core
    methods.  You are, and the system provided class clusters typically
    do, override any of the other methods to provide optimized
    implementations.

    To subclass NSArray, first have a look at the NSArray declaration:

    @interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding>
    - (unsigned)count;
    - (id)objectAtIndex:(unsigned)index;
    @end

    This indicates that you need to override the two declared methods and
    implement the various protocols.  Once done, your NSArray subclass
    will interoperate perfectly well with any API that takes an NSArray.

    If you want to subclass NSMutableArray, you need to implement
    overrides for:

    @interface NSMutableArray : NSArray
    - (void)addObject:(id)anObject;
    - (void)insertObject:(id)anObject atIndex:(unsigned)index;
    - (void)removeLastObject;
    - (void)removeObjectAtIndex:(unsigned)index;
    - (void)replaceObjectAtIndex:(unsigned)index withObject:(id)anObject;
    @end

    If you fail to implement any one of the methods provided in the
    abstract base class of the class cluster, a mighty complaint will be
    raised at runtime if said method is executed.

    b.bum
  • On Feb 10, 2008, at 18:28, Bill Bumgarner wrote:

    >> -- Someone did an automated check of every Cocoa class in Tiger
    >> (maybe it was every subclass of NSObject, I don't remember exactly
    >> now) and counted the number of classes for which the receiver of
    >> [super init] was different from the returned value. The answer was 0.
    >>
    >> That may comfort anyone who wants to leave out the 'self =', but
    >> it's hardly a reliable argument. The number of dead people reading
    >> this discussion is also 0 (afaik), but I wouldn't take that as
    >> proof of immortality.
    >
    > It would be impossible to test every conceivable set of arguments.
    > For example, NSData may return instances of different classes
    > depending on how large a chunk of memory is desired or if it is a
    > memory mapped vs. malloc()d chunk o' bits.
    >
    > Similarly, this is entirely an implementation detail.  Any given
    > class may return a subclass at some future time when it doesn't
    > now.  Or it might even return a different class on a different
    > architecture -- 32 bit vs. 64 bit, for example.

    You (and Jens) are perfectly correct. If I sounded like I thought this
    automated check was anything but snake oil, I expressed myself badly.

    >
    >> -- Some classes cannot be subclassed -- cluster classes like
    >> NSArray, for example. These classes prove nothing one way or
    >> another about writing subclass initializers.
    >
    > Class clusters can be subclassed just fine and, while not terribly
    > common, there are some very good reasons for doing so.
    >

    Well, you're right and I'm wrong.

    But that just makes NSArray even less relevant to the 'self = [super
    init]' question.

    '[[NSArraySubclass alloc] init]' sends the init message to an actual
    instance of NSArraySubclass. The subclass init method (according to
    the Cocoa discussion of NSArray subclassing I just checked) should
    send [super init]. There's nothing special about this, and it's not
    going to answer whether 'self =' is needed.

    (My mistake was in assuming that the substitution of a private class
    occurred inside -[NSArray init]. If it did, you would not be able to
    subclass NSArray. Apparently, the substitution occurs inside +[NSArray
    array...]. My only defense is that everyone else except you who used
    the NSArray example seems to have made the same mistake.)

    Either way, an example based on NSArray doesn't give any information
    about the need for 'self ='.

    But, no matter ...

    On Feb 10, 2008, at 17:49, mmalc crawford wrote:

    > >

    Hooray, a bona fide clincher! :)
  • >
    >> id newSelf = [super init];
    >>
    >> if (self == newSelf) {
    >> ...
    >> } else {
    >> [self release];
    >> self = newSelf;
    >> }
    >>
    >> return self;
    >
    >
    > However, that's assuming super always returns either self or an
    > existing, fully-initialised instance, which as noted previously
    > doesn't have to be the case...
    >
    > Generally, protecting against nil should be quite sufficient, as
    > noted, and in the few cases where a class does do funky voodoo in
    > its initialisers, it should document as such and explain exactly
    > what it could hypothetically do, so you can write your subclasses
    > appropriately.

    I disagree with this example. I think, it's the super class
    responsability to release self.
    The [super init] should be like this:

    {
    [self release];
    return singletonInstance;
    }
  • On 11 Feb '08, at 1:27 AM, Jean-Daniel Dupas wrote:

    > I disagree with this example. I think, it's the super class
    > responsability to release self.

    Correct. Otherwise creating an instance of the superclass would leak
    memory.

    The idiom for indicating failure in an -init method (without raising
    an exception) is:

    if( ! [self setupSomethingCritical] ) {
      [self release];
      return nil;
    }

    —Jens
  • Mostly, Bill, Jens and others have covered the topic.  I'd like to
    hilight a couple of points.

    (1) NSManagedObject *requires* you assign to self.  This is
    documented, not debatable, not stylistic.  On 10.5, you're pretty
    much never going to get back the result of +alloc.  Malcolm sent a
    link to the explanation (*)

    (2) You are quite welcome to subclass from class clusters, although
    it does require some care.

    (*) One of the key assumptions in Wil's original reasoning is that a
    call to super's init could not possibly return anything besides your
    subclass because none of your subclass's ivars would be accessible.
    This is assumption is flawed.

    The super class can ask the Objective-C runtime to tell it everything
    it needs to know about your subclass and its ivars to make things
    right.  Both Core Data and Key Value Observing work with a
    dynamically created subclass of your subclass.

    This assumption is especially flawed under 64 bit with non-fragile
    ivars as the requisite sophistication is much reduced.

    There are other reasons why a super class might return a different
    instance, even if it did give you back your subclass.  For example,
    it could use the extraBytes parameter of NSAllocateObject() or
    otherwise "move" your instance in memory.  Perhaps to a different
    zone, or some other nefarious purpose.
    --

    -Ben
previous month february 2008 next month
MTWTFSS
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29    
Go to today