Re: literal strings - who do they belong to?
Re: literal strings - who do they belong to?
- Subject: Re: literal strings - who do they belong to?
- From: Wade Tregaskis <email@hidden>
- Date: Mon, 13 Jul 2009 17:42:02 -0700
{
NSData *data = [[NSData alloc] initWithBytes:foo length:bar];
const char *bytes = [data bytes];
[data release];
CrashByDoingSomethingWithBytes(bytes);
}
Why should this sort of thing be expected to work, just because the
property in question happens to be an object?
A facetious example, because in reality you never know where pointers
are coming from when returned from some other method. Why should you
ever expect any use of any returned pointer from any object to work?
That there are some well known cases doesn't negate the fact that this
is quite problematic to assume this universally. Completely
impractical, in fact.
One could argue that NSData should [[self retain] autorelease] itself
in that case. But there are valid performance concerns particularly
with NSData, which may be why it doesn't. Certainly it probably
didn't do it originally because no one anticipated it would be a
problem, not because of any deliberate design choice.
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
"... if you need to store a received object as a property in an
instance variable, you must retain or copy it."
Thus, apparentl doing
NSString *s = someobject.somevar;
is essentially against the rules. You should always use
NSString *s = [[someobject.somevar {retain|copy}] autorelease];
(To my eye, not common sense, but understandable)
Note that it says instance variable, not local variable. It should
also so global variable, for completeness; anything outside the scope
of the current function or method.
The general assumption in Cocoa is that the received object is valid
for the rest of your function, which is in fact a simplification (of
it being valid 'til the next autorelease pool or next destructive
change, the latter part being the concession to reality rather than an
intended contract).
In any case, if you want to take it as aggressively as you've
suggested, then you can try that, but it's still wrong; by the time
you get the result it may already have been released. This is the
entire point behind autorelease pools; to allow any object to return
any other object to any other code without the nightmares. As I said
in my original reply, you just can't live happily in Cocoa if you
don't accept this, and follow it.
That many objects don't follow, or correctly follow, this approach is
a sad excuse not to try to do so now and in future. There will always
be exceptional cases, but they can remain exceptional. And many can
be fixed. These sorts of issues can be huge time wasters, I know all
too well. Which is why I'm so strongly advocating you to follow the
policy I suggested.
There's also a high cost, in CPU cycles and memory, to retain/
[auto]release in such an excessively defensive way. And have fun
using Object Alloc to find real problems once you've added these
superfluous retain/release/autoreleases to every return value in your
entire program.
"A received object is normally guaranteed to remain valid within the
method it was received in (exceptions include multithreaded
applications and some Distributed Objects situations, although you
must also take care if you modify the object from which you received
the object). That method may also safely return the object to its
invoker"
This is written quite conservatively to address the fact that a lot of
code is very poorly written in this regard. But again, bad history
doesn't justify bad future. @propertys make it nice and easy to avoid
these issues by doing it right to begin with; being "atomic". There
are certainly valid scenarios where you can justify opting out of
this, but only a few. And you should certainly document them very
carefully. Or better yet, use an alternate but still safe pattern,
such as atomic-retain/copy-return).
However, the last sentence about the method being able to return the
object to its invoker is just bizarre - it glosses over things like
'this method has an autorelease pool', etc. I would have thought
that it makes more sense to simply state that you should [[o retain]
autorelease] anything you want to return at all levels, rather than
implicitly insisting that the bottom level will have done it.
That document is written at a very introductory level and assumes if
you're conscious of autorelease pools, you're aware of their caveats.
Beyond that, what you've suggested is overly defensive again.
Sometimes necessary for complex control flow (e.g. if you yourself
return the raw bytes of an NSData, or if you are indeed escaping from
the topmost autorelease pool), but the cost of all those autoreleases
will rapidly add up to something significant in many programs.
Getting it right at the originator is simpler, and more efficient -
for all you know any given method returns a constant (or any other
kind of immortal object), so all those retains and autoreleases you
layer on top of it are a complete waste of time.
Whether they're cached or singletons or whatever else, they must
obey the policy that they'll stick around 'til the next flush of
the current thread's current autorelease pool. You just can't
safely handle the memory management otherwise, especially in
multithreaded cases.
My assertion there was ambiguous, it's true; I meant that you must do
so if you want to retain your sanity, not that this is what everyone
in fact does, nor has done to date. As we've seen already through
examples.
This is exactly the misconception that needs to be avoided. Here is
a trivial counter example:
id obj = [dict objectForKey:@"whatever"];
[dict release];
NSLog( @"this will crash %@", id );
No autorelease pool, no remaining valid until the current
autorelease pool is flushed.
And if you extrapolate this example further to the case where [dict
release] is called on another thread, you're in a real pickle.
Ideally, objectForKey: would retain]autorelease]. There's perhaps a
fear that changing the behaviour now would be too severe a performance
change for such a fundamental class. Such is life. This shouldn't
prevent you using retain]autorelease] in any of your own, new code.
It is not optimising away the return [[foo retain] autorelease],
which is explicitly allowed in the rules ("That method may also
safely return the object to its invoker"), it is assuming something
about the lifetime of the object you don't own that gets people in
to trouble.
As I stated and have reiterated here, coding so defensively is just
plain bad for your mental well-being. We have to live with the
imperfect world, but the point of this is what you can do in your
code, which is to say: you can do it right, instead. Don't stress so
much over the edge cases; learn them and work around them, and don't
propagate them.
Wade
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden