Re: How to allocate and delegate; or how to keep GC and the static analyzer happy
Christiaan Hofman <[email protected]>
| Newsgroups | gmane.comp.macosx.devel |
|---|---|
| Message-ID | <[email protected]> |
On Oct 9, 2009, at 12:27, Glen Low wrote:
> Hi All
>
> I have several cases of the following pattern in my code:
>
> - (void)start
> {
> [[Something alloc] initWithDelegate:self];
> }
>
> - (void)finishWithSomething:(Something*)something
> {
> [something release];
> }
>
> The intent of course is that the Something object calls back the
> caller with finishWithSomething: and that does the cleanup. Several
> UI classes can work this way e.g. -[UIActionSheet
> initWithTitle:delegate:...].
>
> However as I understand it, there are several problems with this
> pattern:
>
> 1. The code is not GC friendly as between the end of start and the
> beginning of finishWithSomething, there are no references to the
> object, so it may be collected.
> 2. The static analyzer in Xcode 3.2 doesn't like the construction,
> thinking that the object is leaking from the start method.
>
> Of course I could keep a reference to the something object in a
> field of the calling object, but I was hoping to avoid that -- the
> calling object may invoke start multiple times before each
> finishWithSomething:.
>
> Any suggestions?
>
>
>
>
>
>
> Cheers, Glen Low
This code is very wrong, both with managed memory and with GC. You
MUST read the memory management rules in the docs.
The basic rule is that each object instance that should be living
should have an (at least one) owner. The docs tell you how you can see
when a caller gets and relinquishes ownership. The owner is
responsible for 'releasing' the object when it's done. In your sample
code, it seems like the receiver of -finishWithSomething: releases the
Something instance, but it's not the owner, as it even doesn't have a
reference to it. So this breaks these simple rules. Moreover, in -
start it creates an instance that in managed memory is retained, but
it does not take ownership over it (by keeping a reference to it), so
that also breaks the rules.
So the normal procedure is that the object that creates the (retained)
instance claims ownership and keeps a reference to it (directly or
indirectly), and it then releases ownership when it's done with that
object. That will guarantee that objects won't be deallocated or
collected while it's still in use.
There are some cases where the object that creates the instance does
not want to manage it further, e.g. for a temporary message window
like an alert (or its controller). In that case typically the instance
takes ownership over itself during the time it's in use. E.g. NSWindow
can do this when you turn on isReleasedWhenClosed. The way to do this
is that when the instance is 'shown' (or already when it's created) it
'retains' itself, and it 'releases' itself again when it closes. It
would be wrong to leave the release to some delegate, because the
delegate isn't the owner. When using GC, using -retain and -release
would not work for this, because those don't do anything, so you
should use CFRetain and CFRelease.
Christiaan