Re: Accessors (can we close it this way?)
Re: Accessors (can we close it this way?)
- Subject: Re: Accessors (can we close it this way?)
- From: Marco Scheurer <email@hidden>
- Date: Wed, 7 Aug 2002 17:29:06 +0200
Nice effort to have the last word, but I have to disagree.
On Wednesday, August 7, 2002, at 04:22 pm, Ondra Cada wrote:
Very right. And, in Cocoa with its autorelease pools, unless
documentation explicitly says otherwise,
THE VENDOR'S RESPONSIBILITY IS TO ENSURE THAT THE VALUE KEEPS VALID AT
LEAST FOR THE SCOPE OF THE CURRENT AUTORELEASE POOL
This is impossible. All it takes is a release from the client to break
that pompous statement.
Ah but you're not supposed to release you'll say. Well, that's right:
you're not supposed to release your object and then use it without
retaining first. That is the advocated usage of retain/release, and has
been for years.
I don't want to discuss again and again whether this contract is
reasonable (as I think, for good reasons) or irrational (as Marcel
thinks,
for perhaps even better reasons, though to me they don't seem such).
Nevertheless, in Cocoa, it just is so:
On Wednesday, July 31, 2002, at 07:56 , Ali Ozer wrote:
The API contract for most Cocoa APIs is that the following is
valid: (**)
{
str = [someObj title];
... do other stuff ...
... access str ...
}
It's not so yet, and even Ali can make mistakes. Until today, Cocoa
documentation says exactly the contrary.
For me, it's another of Cocoa great features. For Marcel, it probably
is another of Cocoa design bugs. Any road, it is just so. That does not
mean all classes should do so; OTOH, it *does* mean that classes which
DO NOT do so *should be documented as such*.
(In current Cocoa frameworks, for historical reasons, they often are
not. That is considered a documentation bug and should be eventually
fixed.)
I hope not!
The usefulness of this pattern has been grossly exagerated anyway. When
people like Georg say that the simple accessor works 95% of the time,
they are underestimating, because it is probably more like 99.5% of the
time. To prove my point, go through any body of code and count the
retain/release or retain/autorelease that had to be written because an
operation could have freed an object between the time when it's accessed
and the time when it is used.
It happens twice in Sketch, in both case when accessing collection
objects,
It happens once in TextEdit, to retain a view from a disposed window
It happens 6 times in Omni's AppKit extension framework for collection
related objects, once in another case (because of a removeFromSuperview)
This pattern is almost totally useless.
Furthermore, a client shouldn't have to distinguish between "simple
collections" and "complex objects" or whatever else in order to know
what to do.
(a) with a simple container, the methods are straighforward. You *do*
know that -count or -objectEnumerator won't ever release any of the
contained objects.
No kidding? Of course objectEnumerator will not release the object, the
problem is with removeObject: and co.
With eg. a window title, though, you *don't* know whether *any* method
might not, for some arcane implementation-hidden reason, release the
title (and, for example, replace it with another string of the same
contents).
Then you have to know what you want to do and code appropriately instead
of relying on arbitrary objects staying around just long enough.
(b) with simple containers, we HAVE TO put up with the inconvenient
case of "not fulfilling the (**) contract" for efficiency reasons.
It should be emphasized that the difference can be VAST! A benchmark
I've rigged up and Marcel considerably improved showed that
- in normal average code, the plain getter (return ivar) can be roughly
TEN TIMES slower than the safe one (return [[ivar retain] autorelease]);
That's the other way around of course. Which shows that it is not just
useless, but harmful.
Still though there are classes -- in my personal experience, a majority
of them! -- usage of whose getters in any sensible design would stay in
tens,
at the very worst low hundreds: things like NSTask, NSWindow,
NS...Views,
or even the lightweight NSCells. For such classes, I would advocate
usage of the safe getter (return [[ivar retain] autorelease]) for
either conveniency (should you otherwise go the (a) way below) or
safety (should you otherwise go the (b) way below).
It's a bad idea because it's inconsistent. You now have to know, or
guess, how your accessor is implemented.
(b) convenient, very often used, but with plain getters inherently
unsafe (***)
id o=[foo getter];
... something ...
[o whatever];
This code *MOSTLY* works, I do agree with Marcel that cases when it
does not are *RARE*.
That's is not true. The plain getter is simple and cannot be wrong. If
something breaks it's the client code. There's no way to protect against
all abuse. Or else, why release at all? Keeping everything around is
"inherently" safer, at a cost of performance.
You seem to think that "something" is a force of nature, something that
you do not control. That's not the case: it is code that YOU write.
The problem is that with complex classes (of foo) you CAN NEVER KNOW in
which conditions the problem might occur (and therefore you CAN'T say
"here it's safe, no need for (a) patterns"). Not even "not using foo in
...something..." is absolutely safe, for the class just *might*, say,
respond to some notification (posted by ...something...) by sending the
setter to itself!
Not true in single threaded case, where you know. In the multi threaded
case, more is needed anyway. Please stop making this issue so scary and
mysterious. This is engineering, not vaudou.
So, the problem is that if you use plain getters with this client
pattern,
the application might crash quite unexpectedly eg. with a future
bug-fix framework release: the reason would be that you used a wrong
code, which would crash only "RARELY". Myself, I prefer code which
won't crash -- even if it is somewhat slower.
and possibly wrong, because the crash was a symptom that you were using
some obsolete value. Now you can happily use your out of date title
without noticing.
Plus, it's not true that the bad code would crash rarely: it would crash
pretty often, and should be detected easily. Another way to avoid these
cases would be to set pointers to nil when released, everywhere.
Messages to nil do not crash, but their effect can generaly be seen
quickly enough. Another thing is to use NSZombies.
Marcel advocates that for the consistency sake it should be so with ALL
classes. I do agree that it would be consistent; I don't think that
would be a good idea though for it would mean we are back in the (a)
solution for *any* class, and the inconvenience of it would be (from my
subjective point of view) much worse than the gain of the consistency.
OTOH, I'd like to point out again that the safe getter can be
CONSIDERABLY slower, and thus should not be used if there is a
possibility of a really heavy usage.
And I agree with Marcel. And the "safe" getter is an abomination, and
should not never be used.
Marco Scheurer
Sen:te, Lausanne, Switzerland
http://www.sente.ch
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.