{
    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 (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to