Re: Memory Management
Re: Memory Management
- Subject: Re: Memory Management
- From: Marcel Weiher <email@hidden>
- Date: Thu, 31 Jul 2003 02:23:29 +0100
[..]
This is thinking in another language, and forcing Foundation /
Objective-C into that mindframe. It doesn't help putting "garbage
collection" as a concept or a goal to be aspired to, into this mail
thread.
Where on earth did you get that from?!? Reference counting is a form
of garbage collection. Retain/release, of course, is not fully
automatic garbage collection, because it is at best semi-automatic.
The problem that autorelease pools actually solve is that of returning
values from one context to another when there are no further
outstanding references.
What other systems that employ reference counting garbage collectors
(such as earlier Smalltalks) would do is increment the reference count
of the returned value when in transit to the caller, and then decrement
it again once it has been received by the caller's context (where the
refCount was incremented due to assignment). If you didn't do that,
you would lose the object on the way back (see above).
Of course, you can't actually do that with Objective-C with library
code, because you don't have the same kind of control over the
semantics of method/function calls. Autorelease pools solve that
problem very nicely, by extending the life of the object enough so it
can survive the 'limbo' while it is being returned.
Anyway, all this is only necessary when the callee does not hang on to
the object. If the callee does hang on to the object, there simply is
no *need* to extend the life of the object.
[examples]
What I'd like to add is this. Sharing a return value between caller
and method ideally should be fail safe.
There are several levels to this statement.
At the most general level you are asking for referential transparency,
something you are never ever going to get in an imperative language,
and Objective-C is always going to remain an imperative language.
However, I think it is very good that you did bring it up in this
generality, because I think that this is the (somewhat hidden)
motivation behind some of the things that are being said. I find this
motivation understandable, because referential transparency is a nice
thing to have. However, the only way to actually get referential
transparency is to have a language designed from the ground up to
support it. It is one of these things that you absolutely cannot bolt
on to a language after the fact. How this usually works is that there
are *no* references whatsoever, only values. So there can never be
aliasing of pointers/references because there are no
pointers/references. Conceptually, everything is copied all the time.
So if you insert an element into a collection, a new collection is
created that has the element inserted at the right spot. The old
collection is not modified. Nothing is ever modified.
*That* is how you get referential transparency. Autorelease pools
don't cut it, not by a long shot. As you can imagine, it is expensive
like hell, and the languages that have it have to optimize like crazy
to safely transform all these copies into imperative updates.
To be fail safe the returned value must be under ownership of the
caller.
If the caller wants *ownership* then it must retain the result. The
object ownership rules are very clear about this. In fact, this is one
of the points I find highly dubious about the whole
autoreleasing-accessors idea: instead of making it clear that you now
want ownership of that object, you rely on it being in
autorelease-limbo without need.
[..]
If the memory is owned by the caller, then there is no problem, the
caller temporarily grants read/write access to its memory area, and
can peruse it at will on return:
- (void) getName:(NSMutableString *) s
{
[s setString:name_];
}
-(void)getName:(NSMutableString*)s
{
[s release];
}
And the release could just as well happen because of a side effect of
what you are doing. This is no more safe than the return. And on the
other hand, the return is just as safe, because it is 'sharing' between
*objects* not method contexts.
If the memory is solely owned by the method (instance), this means a
weakening in the fail-safeness
(a) it isn't fail safe and (b) sharing between objects is just as
safe/unsafe.
- (NSString *) name
{
return( name_);
}
This transfers a pointer to memory, not owned by the caller.
In the first case, you also transfered a pointer to memory, from the
caller to the callee. This is just as unsafe because the callee could
have released the memory. There is no difference!
If name_ gets set to something else as a side effect to another call
to that instance, I can expect crashes, because the previously
returned value might now point to invalid memory.
Yes, that is the fundamental feature of sharing. If you don't want
that, you need referential transparency (see above).
This method provides therefore only a limited kind of read access to
the return value. That limited access says, you may access that value,
if you know, that it hasn't changed in the mean time. (In 99% of all
cases, I think you will be able to say, yes I can do that :),
otherwise you'd have problems using NSMutableArrays)
Or anything that is imperative programming for that matter...
[callee doing extra retain]
The reason why you get these funny problems when name_ is mutable is,
because you are not yielding access completely, the contents are still
subject to change by the messaged instance. That's why using copy]
autorelease] would be proper, as it also gives the full access rights
of the return value contents to the caller (within the scope... bla
bla).
Once again, being mutable and sharing is inherent in an imperative
programming language. The fact that you are saying one should be
copying values is a strong indication that you are after referential
transparency. I can just repeat that you aren't going to get it in
Objective-C. ever, and you'll waste a lot of cycles not getting it...
;-)
I am not promoting the use of retain] autorelease] (I dislike slowness
:)), but I don't mind it to be the documentation default.
I do.
1) It creates more problems than it solves (masking/delaying erros
until the autorelease pool pops)
2) The problem it 'solves' is exceedingly rare in practice
3) It actually doesn't even 'solve' that, because important cases are
exceptions (collections, 'performance critical' code)
4) The real solution in those exceedingly rare cases that need it is
trivial: let the caller retain
5) Having the retain in the calling code makes it intention revealing,
showing what you actually wanted to do at that point
6) Due to (4) it complicates the rules instead of simplifying them
7) It also complicates object lifetimes / the object ownership rules
8) It creates expectations of "safety" that an imperative language can
never fulfill
9) The cases where it applies are cases where you need to think about
your sharing semantics anyhow (see 5)
10) The performance cost (factor 10) is quite significant, and
non-localized
11) KISS says: just say no! ;-)
That being said, I have no problem with it being listed as an option,
and people deciding what they want to do. However, I do have a problem
with it being listed as the default, most of all because of (8. Each
time this subject comes up and more and more really smart people fall
into similar traps about the 'safety' of the technique convinces me
more and more that there is a real problem.
Marcel
--
Marcel Weiher Metaobject Software Technologies
email@hidden www.metaobject.com
Metaprogramming for the Graphic Arts. HOM, IDEAs, MetaAd etc.
1d480c25f397c4786386135f8e8938e4
_______________________________________________
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.