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.



