Re: [Foo new] vs [[Foo alloc] init]:

  • Keith Duncan wrote:

    >> As far as I understand, [Foo new] is exactly equivalent to [[Foo
    >> alloc] init]
    >
    > Correct.

    Incorrect. Based on the documentation new (by default) does setup
    work analogous to alloc and then invokes init before returning the
    object. It does not actually use the alloc method, though, which
    means it's entirely possible for new to not be equivalent to an alloc/
    init pair.

    > Chaining alloc and init is just the in vogue convention as far as I
    > know. I've been told that -new used to be a popular way of doing it
    > back in the days of NeXTStep.

    It's more than just the 'in vogue convention' though. The point was
    to recognize that allocation and initialization are distinct actions
    and there's benefit in having them be separable. With alloc/init you
    can change one of the phases without concern for the other.
  • On Feb 15, 2008, at 4:55 PM, Gregory Weston wrote:

    > Keith Duncan wrote:
    >
    >>> As far as I understand, [Foo new] is exactly equivalent to [[Foo
    >>> alloc] init]
    >>
    >> Correct.
    >
    > Incorrect. Based on the documentation new (by default) does setup
    > work analogous to alloc and then invokes init before returning the
    > object. It does not actually use the alloc method, though, which
    > means it's entirely possible for new to not be equivalent to an
    > alloc/init pair.

    The only ways it could be different are:

    1. If +new does not in fact call +alloc, but only does something
    equivalent, then if a subclass overrode the +alloc method but didn't
    also override +new, then it would behave differently.  Do any classes
    actually override +alloc?  If so then that might be a good reason not
    to use +new.

    2. If a class overrode +new to do something different.  In that case
    though the class's author probably did that on purpose, so you'd
    probably still be correct to call +new.

    >
    >
    >> Chaining alloc and init is just the in vogue convention as far as I
    >> know. I've been told that -new used to be a popular way of doing it
    >> back in the days of NeXTStep.
    >
    > It's more than just the 'in vogue convention' though. The point was
    > to recognize that allocation and initialization are distinct actions
    > and there's benefit in having them be separable. With alloc/init you
    > can change one of the phases without concern for the other.

    I never meant to imply that there isn't a benefit to having them be
    separable.  It's very common to want to use a different initializer
    than -init, or to want to allocate an object in some other way than
    what +alloc does.  I'm only talking about the common case where you
    ARE just chaining +alloc and -init together, and wondering why people
    don't just use +new instead.
  • On Feb 15, 2008, at 1:55 PM, Gregory Weston wrote:
    > Keith Duncan wrote:
    >>> As far as I understand, [Foo new] is exactly equivalent to [[Foo
    >>> alloc] init]
    >> Correct.
    > Incorrect. Based on the documentation new (by default) does setup
    > work analogous to alloc and then invokes init before returning the
    > object. It does not actually use the alloc method, though, which
    > means it's entirely possible for new to not be equivalent to an
    > alloc/init pair.

    Possible?  Sure.  But one would be mighty optimistic to make such a
    subtle distinction.

    On Leopard, +new is implemented as return [[self alloc] init];  It is
    equivalent unless a subclass overrides, which is exceedingly unlikely.

    b.bum
  • On Feb 15, 2008 11:11 PM, Bill Bumgarner <bbum...> wrote:

    > On Leopard, +new is implemented as return [[self alloc] init];  It is
    > equivalent unless a subclass overrides, which is exceedingly unlikely.

    And, I would have thought, exceedingly rash. What would be gained by
    implementing a non-equivalent behaviour that would outweigh the
    potential for confusion and error?

    Hamish
  • On Feb 15, 2008, at 3:26 PM, Hamish Allan wrote:
    > On Feb 15, 2008 11:11 PM, Bill Bumgarner <bbum...> wrote:
    >
    >> On Leopard, +new is implemented as return [[self alloc] init];  It
    >> is
    >> equivalent unless a subclass overrides, which is exceedingly
    >> unlikely.
    >
    > And, I would have thought, exceedingly rash. What would be gained by
    > implementing a non-equivalent behaviour that would outweigh the
    > potential for confusion and error?

    Taking a relatively simple problem and making it mentally challenging
    enough to hold the attention of a talented software engineer?

    Seriously -- I can't really think of any reason to override +new to do
    something goofy that isn't, otherwise, an *exact* cover for +alloc/-
    init of the same class.

    That is, if you need custom allocation behavior, override +alloc.  If
    you need custom initialization, override -init.

    If you need both, override +alloc and -init ... +new will "just work".

    The documentation is actually a little confusing on this one.  It
    implies that +new is sometimes re-implemented in subclasses in the
    initial sentence on the topic, but the rest of the write-up is how it
    is actually implemented as +new... where the "..." implies "with a set
    of arguments that provide custom initialization".

    b.bum
  • On Feb 15, 2008, at 6:11 PM, Bill Bumgarner wrote:

    > On Feb 15, 2008, at 1:55 PM, Gregory Weston wrote:
    >> Keith Duncan wrote:
    >>>> As far as I understand, [Foo new] is exactly equivalent to [[Foo
    >>>> alloc] init]
    >>> Correct.
    >> Incorrect. Based on the documentation new (by default) does setup
    >> work analogous to alloc and then invokes init before returning the
    >> object. It does not actually use the alloc method, though, which
    >> means it's entirely possible for new to not be equivalent to an
    >> alloc/init pair.
    >
    > Possible?  Sure.  But one would be mighty optimistic to make such
    > a subtle distinction.

    Mighty optimistic? I'd think "appropriately pessimistic" would be a
    better description.

    > On Leopard, +new is implemented as return [[self alloc] init];  It
    > is equivalent unless a subclass overrides, which is exceedingly
    > unlikely.

    Where's the documentation that says that's reliable?

    Without that guarantee, there's a different between saying "this
    statement accurately describes all current implementations" and "this
    statement is correct." It is not correct in general to say that "[Foo
    new] is exactly equivalent to [[Foo alloc] init]." Those are the kind
    of assumptions that burn you after you've forgotten you made them in
    the first place.
  • On Feb 16, 2008, at 4:56 AM, Gregory Weston wrote:
    > Without that guarantee, there's a different between saying "this
    > statement accurately describes all current implementations" and
    > "this statement is correct." It is not correct in general to say
    > that "[Foo new] is exactly equivalent to [[Foo alloc] init]." Those
    > are the kind of assumptions that burn you after you've forgotten you
    > made them in the first place.

    In general, I agree.  In this particular case, I'm not concerned.  If
    Apple were to change the implementation of +new to *not* do +alloc
    followed by -init, many many bits of code would break.  Binary
    compatibility between release of Mac OS X is critical and, thus, this
    particular behavior cannot change.

    Reading the docs, it explicitly states:

    ...
    This method is a combination of alloc and init. Like alloc, it
    initializes the isa instance variable of the new object so it points
    to the class data structure. It then invokes the init method to
    complete the initialization process.
    ...

    From there, the documentation entirely discusses implementing +new...
    as a cover for various -initWith... style initializers.

    Thus, the bare behavior of +new is quite thoroughly well defined as a
    combination of +alloc and -init (in the docs, the the two method names
    are links to the documentation of said methods).

    b.bum
  • On Feb 16, 2008, at 12:11 PM, Bill Bumgarner wrote:

    > On Feb 16, 2008, at 4:56 AM, Gregory Weston wrote:
    >> Without that guarantee, there's a different between saying "this
    >> statement accurately describes all current implementations" and
    >> "this statement is correct." It is not correct in general to say
    >> that "[Foo new] is exactly equivalent to [[Foo alloc] init]."
    >> Those are the kind of assumptions that burn you after you've
    >> forgotten you made them in the first place.
    >
    > In general, I agree.  In this particular case, I'm not concerned.
    > If Apple were to change the implementation of +new to *not* do
    > +alloc followed by -init, many many bits of code would break.
    > Binary compatibility between release of Mac OS X is critical and,
    > thus, this particular behavior cannot change.

    I'm afraid I don't understand that assertion at all. What does this
    have to do with binary compatibility? Why did it *not* break
    compatibility in the move from 10.4 (in which new does not invoke
    alloc by default) to 10.5 (in which it does)?

    > Reading the docs, it explicitly states:
    >
    > ...
    > This method is a combination of alloc and init. Like alloc, it
    > initializes the isa instance variable of the new object so it
    > points to the class data structure. It then invokes the init method
    > to complete the initialization process.
    > ...
    >
    > From there, the documentation entirely discusses implementing
    > +new... as a cover for various -initWith... style initializers.
    >
    > Thus, the bare behavior of +new is quite thoroughly well defined as
    > a combination of +alloc and -init (in the docs, the the two method
    > names are links to the documentation of said methods).

    It's well defined as being functionally equivalent by default to
    alloc and init. It is even defined, as I had already noted, as
    invoking init specifically. Given that explicitness, I would argue
    that it's likely meaningful that the documentation does *not*
    explicitly indicate that it invokes alloc. In fact the implication of
    "Like alloc, ..." is very strongly that it's only guaranteed to be an
    equivalent, not a wrapper. And since any developer who wants can
    override alloc and new at any time they like, even though Apple
    *does* implement new to invoke alloc by default it cannot be reliably
    said to do so.
  • On Feb 16, 2008, at 1:16 PM, Gregory Weston wrote:
    > On Feb 16, 2008, at 12:11 PM, Bill Bumgarner wrote:
    >> On Feb 16, 2008, at 4:56 AM, Gregory Weston wrote:
    >>> Without that guarantee, there's a different between saying "this
    >>> statement accurately describes all current implementations" and
    >>> "this statement is correct." It is not correct in general to say
    >>> that "[Foo new] is exactly equivalent to [[Foo alloc] init]."
    >>> Those are the kind of assumptions that burn you after you've
    >>> forgotten you made them in the first place.
    >>
    >> In general, I agree.  In this particular case, I'm not concerned.
    >> If Apple were to change the implementation of +new to *not* do
    >> +alloc followed by -init, many many bits of code would break.
    >> Binary compatibility between release of Mac OS X is critical and,
    >> thus, this particular behavior cannot change.
    >
    > I'm afraid I don't understand that assertion at all. What does this
    > have to do with binary compatibility? Why did it *not* break
    > compatibility in the move from 10.4 (in which new does not invoke
    > alloc by default) to 10.5 (in which it does)?

    I just checked all the way back to 10.1. The implementation was
    +allocWithZone: NULL followed by -init until Leopard, when it moved to
    +alloc followed by -init.

    If the behavior had changed to anything that avoided either of those
    methods (keeping in mind that +alloc is a cover for +allocWithZone:
    until the latter was effectively deprecated as zones have fallen out
    of recommended practice), then there would have been a binary
    compatibility issue.  There is only one version of any given framework
    on any given release of Mac OS X.  As such, Leopard's Foundation /
    AppKit has to remain compatible with prior implementations to the
    point where just about all applications -- a handful have to be rev'd
    mostly because they broke the rules, whether by bug or intentional --
    remain compatible across releases of Mac OS X.

    (There is an exception -- some frameworks will sometimes check to see
    if a particular application is "linked for" a particular version of
    the system and, as a result, will modify their behavior slightly.
    This is generally done to preserve bugs that existed in previous
    systems.  For example, the AppKit might preserve a memory leak that
    was present in Tiger or Panther simply because some third party apps
    had worked around the leak with an extra -release and the now-fixed
    version of the AppKit will cause those applications to break.)

    >> Reading the docs, it explicitly states:
    >>
    >> ...
    >> This method is a combination of alloc and init. Like alloc, it
    >> initializes the isa instance variable of the new object so it
    >> points to the class data structure. It then invokes the init method
    >> to complete the initialization process.
    >> ...
    >>
    >> From there, the documentation entirely discusses implementing
    >> +new... as a cover for various -initWith... style initializers.
    >>
    >> Thus, the bare behavior of +new is quite thoroughly well defined as
    >> a combination of +alloc and -init (in the docs, the the two method
    >> names are links to the documentation of said methods).
    >
    > It's well defined as being functionally equivalent by default to
    > alloc and init. It is even defined, as I had already noted, as
    > invoking init specifically. Given that explicitness, I would argue
    > that it's likely meaningful that the documentation does *not*
    > explicitly indicate that it invokes alloc. In fact the implication
    > of "Like alloc, ..." is very strongly that it's only guaranteed to
    > be an equivalent, not a wrapper. And since any developer who wants
    > can override alloc and new at any time they like, even though Apple
    > *does* implement new to invoke alloc by default it cannot be
    > reliably said to do so.

    The doc says "a combination of alloc and init" with the method names
    hyperlinked to their actual documentation.  I'm not sure how that can
    be read to mean anything but that the methods are being invoked.

    b.bum
  • On Feb 16, 2008, at 6:28 PM, Bill Bumgarner wrote:

    > If the behavior had changed to anything that avoided either of
    > those methods (keeping in mind that +alloc is a cover for
    > +allocWithZone: until the latter was effectively deprecated as
    > zones have fallen out of recommended practice), then there would
    > have been a binary compatibility issue.

    You've repeated the assertion without actually explaining it. How
    would it be a binary compatibility issue for new to either start or
    stop directly invoking alloc or allocWithZone: rather than simply
    performing comparable operations? If I can override +alloc to do
    whatever I like as long as it performs a certain minimum required set
    of actions, why would be be troublesome for +new to not directly
    invoke +alloc as long as it performed that minimum required set of
    actions?

    >>> Reading the docs, it explicitly states:
    >>>
    >>> ...
    >>> This method is a combination of alloc and init. Like alloc, it
    >>> initializes the isa instance variable of the new object so it
    >>> points to the class data structure. It then invokes the init
    >>> method to complete the initialization process.
    >>> ...
    >>>
    >>> From there, the documentation entirely discusses implementing
    >>> +new... as a cover for various -initWith... style initializers.
    >>>
    >>> Thus, the bare behavior of +new is quite thoroughly well defined
    >>> as a combination of +alloc and -init (in the docs, the the two
    >>> method names are links to the documentation of said methods).
    >>
    >> It's well defined as being functionally equivalent by default to
    >> alloc and init. It is even defined, as I had already noted, as
    >> invoking init specifically. Given that explicitness, I would argue
    >> that it's likely meaningful that the documentation does *not*
    >> explicitly indicate that it invokes alloc. In fact the implication
    >> of "Like alloc, ..." is very strongly that it's only guaranteed to
    >> be an equivalent, not a wrapper. And since any developer who wants
    >> can override alloc and new at any time they like, even though
    >> Apple *does* implement new to invoke alloc by default it cannot be
    >> reliably said to do so.
    >
    > The doc says "a combination of alloc and init" with the method
    > names hyperlinked to their actual documentation.  I'm not sure how
    > that can be read to mean anything but that the methods are being
    > invoked.

    I read it as indicating that it's a *functional* combination of those
    two methods. I read the lack of explicit comment that it invokes
    alloc as significant in context of the presence of the explicit
    comment that it invokes init, bolstered by the indication that it
    acts "like" alloc. The 10.4 documentation includes the precise
    phasing you've excerpted and yet under 10.4.x new definitely does not
    invoke alloc by default. I stand by my original comment in this
    thread; the proposition ...

    "[Foo new] is exactly equivalent to [[Foo alloc] init]"

    ... is false. It wasn't correct by default before 10.5 and it's not
    reliable in any version of OS X. You can make it true by adding
    additional conditions, and you can question the sanity of the
    individual developer who makes it untrue for any of their classes (in
    which case I'd likely join you), but the proposition is not true as
    presented. Assuming it *is* true - that somehow new by definition
    must invoke alloc - is an error and relying on that assumption can
    get you into trouble. For example, if you support deployment on
    Tiger, override +alloc in one of your classes and use +new to
    instantiate that class, your override will not be used. That's all
    I've been trying to say.
  • On Feb 17, 2008, at 5:25 AM, Gregory Weston wrote:
    > You've repeated the assertion without actually explaining it. How
    > would it be a binary compatibility issue for new to either start or
    > stop directly invoking alloc or allocWithZone: rather than simply
    > performing comparable operations? If I can override +alloc to do
    > whatever I like as long as it performs a certain minimum required
    > set of actions, why would be be troublesome for +new to not directly
    > invoke +alloc as long as it performed that minimum required set of
    > actions?

    (1) Developer declares a subclass of NSObject that overrides, say,
    +alloc

    (2) Developer instantiates said class through +new

    (3) Apple changes the implementation of +new in, say, 10.9 to no
    longer call +alloc

    (4) Developer's application that had been compiled on, say, 10.4 and
    has run without recompilation on 10.4, 10.5, 10.6, 10.7 and 10.8 now
    breaks when double-clicked on 10.9

    When Apple released Leopard, there was a general expectation -- a
    general requirement -- that any application compiled/built on prior
    releases of Mac OS X would work on Leopard.

    Any application that does not, as per the above hypothetical example,
    continue to work across a major OS release boundary or a software
    update is considered to be a binary compatibility issue.

    >>>>
    > ... is false. It wasn't correct by default before 10.5 and it's not
    > reliable in any version of OS X. You can make it true by adding
    > additional conditions, and you can question the sanity of the
    > individual developer who makes it untrue for any of their classes
    > (in which case I'd likely join you), but the proposition is not true
    > as presented. Assuming it *is* true - that somehow new by definition
    > must invoke alloc - is an error and relying on that assumption can
    > get you into trouble. For example, if you support deployment on
    > Tiger, override +alloc in one of your classes and use +new to
    > instantiate that class, your override will not be used. That's all
    > I've been trying to say.

    I'm not sure where your claim that +new was not implemented by calling
    +alloc/+allocWithZone: followed by -init.  I looked at the source back
    to 10.1.  +new was implemented as a call to +alloc or +allocWithZone:
    followed by a call to -init since at least 10.1.

    The documentation is quite clear -- +new is a combination of +alloc
    and -init.  How that contract could be met without actually calling
    said methods is unclear to me.

    b.bum
  • On Feb 17, 2008, at 11:13 AM, Bill Bumgarner wrote:

    > On Feb 17, 2008, at 5:25 AM, Gregory Weston wrote:
    >> You've repeated the assertion without actually explaining it. How
    >> would it be a binary compatibility issue for new to either start
    >> or stop directly invoking alloc or allocWithZone: rather than
    >> simply performing comparable operations? If I can override +alloc
    >> to do whatever I like as long as it performs a certain minimum
    >> required set of actions, why would be be troublesome for +new to
    >> not directly invoke +alloc as long as it performed that minimum
    >> required set of actions?
    >
    > (1) Developer declares a subclass of NSObject that overrides, say,
    > +alloc
    >
    > (2) Developer instantiates said class through +new

    2a. Developer has made an error in assuming that new will invoke
    alloc, in that the documentation makes no such promise, the framework
    had no such behavior until less than 3 months ago, and that behavior
    cannot be guaranteed even today.

    > (3) Apple changes the implementation of +new in, say, 10.9 to no
    > longer call +alloc
    >
    > (4) Developer's application that had been compiled on, say, 10.4
    > and has run without recompilation on 10.4, 10.5, 10.6, 10.7 and
    > 10.8 now breaks when double-clicked on 10.9

    Incorrect. If the developer built on 10.4, using new to instantiate
    their class did not invoke their override of alloc.

    > When Apple released Leopard, there was a general expectation -- a
    > general requirement -- that any application compiled/built on prior
    > releases of Mac OS X would work on Leopard.

    True. I think it's an eminently reasonable expectation as long as
    there were no unwarranted assumptions made. I assert that your
    assumption that +new is guaranteed to invoke +alloc is unwarranted.
    The documentation says that new act's *like* alloc. It does not
    promise that it will achieve that by invoking alloc, in contrast to
    the promise that it *will* invoke init.

    > Any application that does not, as per the above hypothetical
    > example, continue to work across a major OS release boundary or a
    > software update is considered to be a binary compatibility issue.

    The only issue you've laid out is that a developer relied on
    something that was not documented to be reliable and *cannot* be
    guaranteed to be reliable. Kind of like the handful of developers who
    had trouble with the 10.5 firewall because they made unwarranted and
    incorrect assumptions about the extent of their ownership of the app
    package.

    >> ... is false. It wasn't correct by default before 10.5 and it's
    >> not reliable in any version of OS X. You can make it true by
    >> adding additional conditions, and you can question the sanity of
    >> the individual developer who makes it untrue for any of their
    >> classes (in which case I'd likely join you), but the proposition
    >> is not true as presented. Assuming it *is* true - that somehow new
    >> by definition must invoke alloc - is an error and relying on that
    >> assumption can get you into trouble. For example, if you support
    >> deployment on Tiger, override +alloc in one of your classes and
    >> use +new to instantiate that class, your override will not be
    >> used. That's all I've been trying to say.
    >
    > I'm not sure where your claim that +new was not implemented by
    > calling +alloc/+allocWithZone: followed by -init.  I looked at the
    > source back to 10.1.  +new was implemented as a call to +alloc or
    > +allocWithZone: followed by a call to -init since at least 10.1.

    See what you're saying? +alloc *or* +allocWithZone:.

    See the subject? +alloc. It does not say anything about +allocWithZone:.

    See this code?

    @interface MyTest : NSObject
    @end

    @implementation MyTest
    + (id)alloc
    {
      NSLog(@"allocating");
      return [super alloc];
    }
    @end

    @implementation MyObject

    - (void)awakeFromNib
    {
      MyTest* a = [[MyTest alloc] init];
      MyTest* b = [MyTest new];
      [a release];
      [b release];
    }

    @end

    Guess how many times it emits "allocating" on 10.4. Hint: It's not
    the same as 10.5.

    I never claimed that "+new was not implemented by calling +alloc/
    +allocWithZone:," so I'm not sure what yu hope to demonstrate by
    saying that I did. What I claimed, and what is true, is that it is
    not correct to say that "[Foo new] is exactly equivalent to [[Foo
    alloc] init]." It's not true by default prior to 10.5, and it's not
    reliable on any version of OS X. If you'd like to say "[Foo new] is
    exactly equivalent to [[Foo alloc] init] as long as you're running
    10.5-10.5.2, using no 3rd-party classes outside your control, did not
    override +new in any of your classes, and are not writing a plugin to
    code outside your control" I won't argue. But that's a much more
    restricted statement than the one under discussion.
  • On Feb 17, 2008, at 11:15 AM, Gregory Weston wrote:
    > See what you're saying? +alloc *or* +allocWithZone:.
    >
    > See the subject? +alloc. It does not say anything about
    > +allocWithZone:.
    >
    > See this code?
    >
    > @interface MyTest : NSObject
    > @end
    >
    > @implementation MyTest
    > + (id)alloc
    > {
    > NSLog(@"allocating");
    > return [super alloc];
    > }
    > @end

    You would have to override +allocWithZone: to have any kind of
    reliable behavior.  +alloc is a cover for +allocWithZone: with a NULL
    zone.  Not that you would know that from the documentation,
    unfortunately.

    Without assuming anything, you would have to override both allocator
    methods;  +alloc and +allocWithZone:.

    b.bum
  • On Feb 17, 2008, at 3:06 PM, Bill Bumgarner wrote:

    > On Feb 17, 2008, at 11:15 AM, Gregory Weston wrote:
    >> See what you're saying? +alloc *or* +allocWithZone:.
    >>
    >> See the subject? +alloc. It does not say anything about
    >> +allocWithZone:.
    >>
    >> See this code?
    >>
    >> @interface MyTest : NSObject
    >> @end
    >>
    >> @implementation MyTest
    >> + (id)alloc
    >> {
    >> NSLog(@"allocating");
    >> return [super alloc];
    >> }
    >> @end
    >
    > You would have to override +allocWithZone: to have any kind of
    > reliable behavior.

    Or you could just not assume that something which hasn't been
    explicitly stated in the docs is guaranteed. That's what I've been
    trying to say: The only promise the documentation makes about new,
    alloc and allocWithZone is that they're peers. It doesn't say a peep
    about any of them invoking any of the others to achieve that
    equivalence. That lack of explicit promise - the utter inability to
    make such a promise in general - is why it's wrong to say without a
    whole lot of caveats that one technique is precisely equivalent to
    another. Looking at the implementation of a method, rather than the
    interface, as it exists today and saying "you can count on this" is
    an outright rejection of one of the core concepts of OO.

    > +alloc is a cover for +allocWithZone: with a NULL zone.  Not that
    > you would know that from the documentation, unfortunately.

    Which means, like the current behavior of new, that's an
    implementation detail that shouldn't really be relied on.

    > Without assuming anything, you would have to override both
    > allocator methods;  +alloc and +allocWithZone:.

    So what you're saying is that [Foo new] isn't actually reliably
    equivalent to [[Foo alloc] init]? That all we can really assume about
    the default implementation of +new is that it performs behavior
    functionally equivalent to that performed by the default +alloc (and
    then invokes init)?
  • On Feb 17, 2008, at 2:28 PM, Gregory Weston wrote:
    > Or you could just not assume that something which hasn't been
    > explicitly stated in the docs is guaranteed. That's what I've been
    > trying to say: The only promise the documentation makes about new,
    > alloc and allocWithZone is that they're peers. It doesn't say a peep
    > about any of them invoking any of the others to achieve that
    > equivalence. That lack of explicit promise - the utter inability to
    > make such a promise in general - is why it's wrong to say without a
    > whole lot of caveats that one technique is precisely equivalent to
    > another. Looking at the implementation of a method, rather than the
    > interface, as it exists today and saying "you can count on this" is
    > an outright rejection of one of the core concepts of OO.

    You can draw that conclusion if you want.

    In light of the documentation, the binary compatibility requirements,
    and the implementation for at least the past 6 years -- if not the
    past 14 -- it would seem a rather pessimistic conclusion.

    >> +alloc is a cover for +allocWithZone: with a NULL zone.  Not that
    >> you would know that from the documentation, unfortunately.
    >
    > Which means, like the current behavior of new, that's an
    > implementation detail that shouldn't really be relied on.

    Hardly the point.  If you are overriding +alloc for whatever reason,
    you better had override +allocWithZone:, too.

    b.bum
  • On Feb 17, 2008 7:15 PM, Gregory Weston <gweston...> wrote:

    > See what you're saying? +alloc *or* +allocWithZone:.
    >
    > See the subject? +alloc. It does not say anything about +allocWithZone:.
    >
    > See this code?
    >
    > [...]
    >
    > Guess how many times it emits "allocating" on 10.4. Hint: It's not
    > the same as 10.5.

    Your argument would be somewhat stronger if your code looked like this:

    @implementation MyTest
    + (id)allocWithZone:(NSZone *z)
    {
      if (z && z != NSDefaultMallocZone())
        NSLog(@"allocating in some way not equivalent to calling plain alloc");
      return [super allocWithZone:z];
    }
    @end

    In other words, +new may not call +alloc, but if it calls
    +allocWithZone: with exactly the same effect, the point is moot. You
    might think of +allocWithZone: as being the "designated allocator"
    (pre-Leopard, at least) -- you would always override that rather than
    +alloc.

    Hamish
  • On Feb 17, 2008, at 2:28 PM, Gregory Weston wrote:

    >> +alloc is a cover for +allocWithZone: with a NULL zone.  Not that
    >> you would know that from the documentation, unfortunately.
    >
    > Which means, like the current behavior of new, that's an
    > implementation detail that shouldn't really be relied on.

    I can't see why this would ever matter much in practice, but if you're
    concerned about the API promise (or lack thereof) for "+new", why not
    fix it by either adding an implementation that you approve of, and
    that you can control, as a category to NSObject, or as a macro?

    @implementation NSObject (BetterNew)
    + (id) betterNew { return [[self alloc] init]; }
    @end

    Problem solved, and as an additional bonus we won't have to suffer any
    more messages about this on the list...

    j o a r
  • On Feb 17, 2008, at 6:05 PM, Bill Bumgarner wrote:

    > On Feb 17, 2008, at 2:28 PM, Gregory Weston wrote:
    >> Or you could just not assume that something which hasn't been
    >> explicitly stated in the docs is guaranteed. That's what I've been
    >> trying to say: The only promise the documentation makes about new,
    >> alloc and allocWithZone is that they're peers. It doesn't say a
    >> peep about any of them invoking any of the others to achieve that
    >> equivalence. That lack of explicit promise - the utter inability
    >> to make such a promise in general - is why it's wrong to say
    >> without a whole lot of caveats that one technique is precisely
    >> equivalent to another. Looking at the implementation of a method,
    >> rather than the interface, as it exists today and saying "you can
    >> count on this" is an outright rejection of one of the core
    >> concepts of OO.
    >
    > You can draw that conclusion if you want.
    >
    > In light of the documentation, the binary compatibility
    > requirements, and the implementation for at least the past 6 years
    > -- if not the past 14 -- it would seem a rather pessimistic
    > conclusion.

    The documentation supports me more than you, the binary compatibility
    "requirements" don't exist, and the implementation aside from being
    irrelevant only supports your reading since October. Yes, it *is* a
    pessimistic stance, but that's the appropriate way to read such
    things. An API doc is a first cousin to a standards document; what
    isn't said is as important as what is and, in particular, if a
    behavior isn't promised as reliable then relying on it is an error.

    You were correct earlier when you noted that its in Apple's interest
    to support correctly written programs, and historically they've done
    that to a fault - I've seen them let things linger so long between
    deprecation and removal that people decided Apple was kidding and
    started using them again. But they've also not been terribly
    forgiving of developers who relied on something that was never
    documented as reliable. The behavior we're talking about here falls
    into that latter category.

    There is not a peep anywhere in the documentation that says alloc
    *will* invoke allocWithZone: or that new will invoke either of them.
    That lack of guarantee carries more weight than the implementation at
    any given moment. The fact that the implementation changed in a
    potentially significant way last October should scream about the
    importance of not making that assumption. What happens to the
    developer last year who assumed the then-current implementation of
    new was reliable and leveraged that to intentionally make alloc
    meaningfully different than new? Horrible design, IMO, and the
    assumption is only part of it, but it's completely legal. Are you
    going to claim that their assumption that new would always invoke
    allocWithZone: directly is somehow less valid than your implication
    today that it's always going to invoke either of alloc... methods?

    >>> +alloc is a cover for +allocWithZone: with a NULL zone.  Not
    >>> that you would know that from the documentation, unfortunately.
    >>
    >> Which means, like the current behavior of new, that's an
    >> implementation detail that shouldn't really be relied on.
    >
    > Hardly the point.

    An odd comment, given that the validity of assumptions seems to be
    precisely the point.

    > If you are overriding +alloc for whatever reason, you better had
    > override +allocWithZone:, too.

    The real point is that there's no formal requirement that you do so.
    It only becomes an issue if you make unwarranted assumptions about
    the implementation.
  • On Feb 17, 2008, at 6:47 PM, Hamish Allan wrote:

    > On Feb 17, 2008 7:15 PM, Gregory Weston <gweston...> wrote:
    >
    >> See what you're saying? +alloc *or* +allocWithZone:.
    >>
    >> See the subject? +alloc. It does not say anything about
    >> +allocWithZone:.
    >>
    >> See this code?
    >>
    >> [...]
    >>
    >> Guess how many times it emits "allocating" on 10.4. Hint: It's not
    >> the same as 10.5.
    >
    > Your argument would be somewhat stronger if your code looked like
    > this:
    >
    > @implementation MyTest
    > + (id)allocWithZone:(NSZone *z)
    > {
    > if (z && z != NSDefaultMallocZone())
    > NSLog(@"allocating in some way not equivalent to calling plain
    > alloc");
    > return [super allocWithZone:z];
    > }
    > @end
    >
    > In other words, +new may not call +alloc, but if it calls
    > +allocWithZone:

    Which is promised where?

    > with exactly the same effect,

    It won't be exactly the same effect if a developer decided the prior
    behavior of new - that it did *not* invoke alloc - was reliable and
    leveraged that presumed distinction when they overrode alloc. Can you
    not imagine someone saying, "I'd like alloc to do something special,
    and new to still be available to achieve the default behavior," and
    getting burned when their users started moving to Leopard? You and
    Bill would apparently say it was a bad assumption and they got what
    they deserved - and I'd probably agree - but that assumption is not
    definitively worse than the one we're talking about. It hinges on
    exactly the same error: Someone looked inside the black box and
    decided that it wasn't going to change.

    > the point is moot.

    I'm going to direct your attention to the subject line and the
    proposition that launched this debate as I did for Bill. The question
    is not about the relationship between new and allocWithZone:. It's
    about the relationship between new and alloc, which changed
    significantly in October.

    > You might think of +allocWithZone: as being the "designated allocator"
    > (pre-Leopard, at least) -- you would always override that rather than
    > +alloc.

    You might think of it that way, but you'd have no formal reason to do
    so. The fact that alloc invokes allocWithZone: or that new invokes
    either of them is an implementation detail not exposed anywhere in
    the documentation or interface. Relying on unpromised implementation
    details is anathema to OO design. You can do it if you like, but
    encouraging others to do so, or telling them that it's reliable today
    when it isn't, is inappropriate. That would tend to accelerate the
    onset of the alleged binary compatibility issue that Bill wants to
    avoid.
  • On Feb 18, 2008, at 6:53 AM, Gregory Weston wrote:

    > On Feb 17, 2008, at 6:05 PM, Bill Bumgarner wrote:
    >
    >> If you are overriding +alloc for whatever reason, you better had
    >> override +allocWithZone:, too.
    >
    > The real point is that there's no formal requirement that you do
    > so. It only becomes an issue if you make unwarranted assumptions
    > about the implementation.

    If you're familiar with Cocoa, you know that the documentation is
    somewhat lacking.  That sucks, but on this particular point the
    documentation seems reasonably clear.  The Discussion for alloc
    begins as follows:

    "The isa instance variable of the new instance is initialized to a
    data structure that describes the class; memory for all other
    instance variables is set to 0. The new instance is allocated from
    the default zoneā€”use allocWithZone: to specify a particular zone."

    Ok, it even mentions allocWithZone: right at the beginning, so let's
    read its Discussion:

    "The isa instance variable of the new instance is initialized to a
    data structure that describes the class; memory for its other
    instance variables is set to 0. If zone is nil, the new instance will
    be allocated from the default zone (as returned by
    NSDefaultMallocZone)."

    Now, it's not stated exactly and formally, but if you're familiar
    with Cocoa, you know that it's an extremely common pattern to have
    methods doSomething and doSomethingWithArgument:, where doSomething
    is implemented as doSomethingWithArgument:nil.  Thus, if you're
    familiar with Cocoa, your first thought after reading those
    Discussions above is that maybe alloc is implemented as
    allocWithZone:nil.

    And if you're not familiar with Cocoa, it's not the greatest idea to
    override such a crucial method as alloc.

    -Jeff
  • On 18 Feb 2008, at 13:11, Gregory Weston wrote:

    >> You might think of +allocWithZone: as being the "designated
    >> allocator"
    >> (pre-Leopard, at least) -- you would always override that rather than
    >> +alloc.
    >
    > You might think of it that way, but you'd have no formal reason to
    > do so. The fact that alloc invokes allocWithZone: or that new
    > invokes either of them is an implementation detail not exposed
    > anywhere in the documentation or interface.

    IMO it would be better if +alloc was explicitly documented to call
    +allocWithZone:, and if people were told to override the latter method
    in preference to the former (just filed as <rdar://5749202>).  But I
    think a lot of us always assumed that things worked that way (and that
    we should override +allocWithZone:, not +alloc) because it's the same
    pattern as for -copy/-copyWithZone: and -mutableCopy/-
    mutableCopyWithZone:.

    > Relying on unpromised implementation details

    I think the fact that this particular detail isn't documented is an
    oversight, rather than a conscious decision.  I think that's what Bill
    has been trying to tell you.  It probably *should* be documented,
    which is why I just filed a bug report.

    Likewise, it seems that the fact that +new *wasn't* calling +alloc is
    arguably a bug (albeit a minor one), but since +allocWithZone:NULL
    should always have been equivalent to +alloc, overriding just +alloc
    would have been wrong in any case.

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
  • I think this debate has passed the point of usefulness. It's probably
    best to take it to private email.

    If you feel the documentation is not explicit enough in this case,
    file a bug.

    Despite your assertion, there ARE binary compatibility requirements
    within Apple. Bill is in an excellent position to know this since he's
    an Apple engineer.

    Scott
    Apple Inc
    Technical Publications, Cocoa and Developer Tools Group

    On Feb 18, 2008, at 7:53 AM, Gregory Weston wrote:

    >>
    >> In light of the documentation, the binary compatibility
    >> requirements, and the implementation for at least the past 6 years
    >> -- if not the past 14 -- it would seem a rather pessimistic
    >> conclusion.
    >
    > The documentation supports me more than you, the binary
    > compatibility "requirements" don't exist, and the implementation
    > aside from being irrelevant only supports your reading since
    > October. Yes, it *is* a pessimistic stance, but that's the
    > appropriate way to read such things. An API doc is a first cousin to
    > a standards document; what isn't said is as important as what is
    > and, in particular, if a behavior isn't promised as reliable then
    > relying on it is an error.
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