controllers, delegates, retain, release ...

  • I have some code inherited from someone else (so I have no answers to
    why it's the way it presently is) that's handling retention in a way I
    find confusing, and think is not in accordance with the spirit of the
    law.  But I can't quite figure out how to do it "right".  Is there a
    pattern I should be following, and at what point am I diverging from it?

    The quandary involves two classes, call them "Controller" and "UI".
    Controller is created when the NIB is loaded, and lives effectively
    forever.  From time to time, Controller needs to create an instance of
    UI, to manage some user interaction (take some parameters, do some
    work, show status, you know the drill).  It's possible for there to be
    several UI objects alive and on-screen at once.  My problem concerns
    the retention of the UI objects.

    In the current structure, Controller performs
    {
      UI *ui = [[UI alloc] initWithContext: context];
      [ui run: @selector(doSomething) title: @"Title"];
    }

    That is: the UI object is alloc'ed (creating a release obligation),
    but is not (auto)released here, nor is any reference to it saved
    anywhere so it may be (auto)released later.  Controller has nothing to
    do with releasing this object, which is the bit that sticks in my craw.

    Rather, over in UI we have:

    -(void)windowWillClose:(NSNotification *)aNotification
    {
        [self autorelease];
    }

    That is: after UI has done all it needs to do, and the user clicks the
    final button (connected here), it autoreleases itself, completes, and
    the pool finishes it off.

    This is all working; nothing leaks, nothing is prematurely
    dealloc'ed.  But having the alloc and (auto)release in separate
    classes seems unsound.

    I found an earlier thread in this list,

      http://lists.apple.com/archives/cocoa-dev/2002/Jul/msg01001.html

    which is darned close to my situation (almost, but not quite, totally
    like).  This thread solves its very similar problem by having
    Controller keep a ref to UI, and release it during Controller's
    dealloc.  I can't quite see how to handle that in my case.  A minor
    problem is that I may have an unpredictable number of these UI objects
    about, so I'd need an NSMutableArray of them or something, not just a
    scalar field.  But a bigger problem is that Controller isn't
    dealloc'ed until the end of the program, which is far too late: it's a
    long-lived, server-like program, I'd be accumulating these UI objects
    forever.

      - Is my eternal, shared Controller bogus, and I should be finding
    some way to create multiple Controllers, and destroy them more often?

      - Is there some way to get the windowWillClose event passed back to
    Controller, rather than to UI, so the autorelease can happen there?

      - Is there some other rotation I should consider?

      - Or, is this actually The Way It's Done?

    The code, if you're curious, may be found via Subversion at http://scplugin.tigris.org/svn/scplugin/trunk/
    .  The players are actually:

    "Controller": SCUIDaemonController+SubversionSupport

    "UI": several, but most illustratively NewGenericUI

    -==-
    Jack Repenning
    Chief Technology Officer
    CollabNet, Inc.
    8000 Marina Boulevard, Suite 600
    Brisbane, California 94005
    office: +1 650.228.2562
    mobile: +1 408.835.8090
    raindance: +1 877.326.2337, x844.7461
    aim: jackrepenning
    skype: jrepenning
  • On Feb 22, 2008, at 10:39, Jack Repenning wrote:

    > In the current structure, Controller performs
    > {
    > UI *ui = [[UI alloc] initWithContext: context];
    > [ui run: @selector(doSomething) title: @"Title"];
    > }
    >
    > That is: the UI object is alloc'ed (creating a release obligation),
    > but is not (auto)released here, nor is any reference to it saved
    > anywhere so it may be (auto)released later.  Controller has nothing
    > to do with releasing this object, which is the bit that sticks in my
    > craw.
    >
    > Rather, over in UI we have:
    >
    > -(void)windowWillClose:(NSNotification *)aNotification
    > {
    > [self autorelease];
    > }

    FWIW, I wouldn't call it wrong for an object to manage its own
    lifetime. You'll leak UIs if the notification never appears, but this
    is apparently not a problem.

    You could clarify the code a little bit perhaps by removing the
    appearance of leaking from Controller:

    {
      UI *ui = [[[UI alloc] initWithContext: context] autorelease];
      [ui run: @selector(doSomething) title: @"Title"];
    }

    and putting full control of retention into UI:

    -(id) initWithContext: ...
    {
      ...
      return [self retain];
    }
    -(void)windowWillClose:(NSNotification *)aNotification
    {
        [self autorelease];
    }
  • On Feb 22, 2008, at 20:39, Jack Repenning wrote:

    > I have some code inherited from someone else (so I have no answers
    > to why it's the way it presently is) that's handling retention in a
    > way I find confusing, and think is not in accordance with the
    > spirit of the law.  But I can't quite figure out how to do it
    > "right".  Is there a pattern I should be following, and at what
    > point am I diverging from it?
    >
    > The quandary involves two classes, call them "Controller" and
    > "UI".  Controller is created when the NIB is loaded, and lives
    > effectively forever.  From time to time, Controller needs to create
    > an instance of UI, to manage some user interaction (take some
    > parameters, do some work, show status, you know the drill).  It's
    > possible for there to be several UI objects alive and on-screen at
    > once.  My problem concerns the retention of the UI objects.
    >
    > In the current structure, Controller performs
    > {
    > UI *ui = [[UI alloc] initWithContext: context];
    > [ui run: @selector(doSomething) title: @"Title"];
    > }
    >
    > That is: the UI object is alloc'ed (creating a release obligation),
    > but is not (auto)released here, nor is any reference to it saved
    > anywhere so it may be (auto)released later.  Controller has nothing
    > to do with releasing this object, which is the bit that sticks in
    > my craw.
    >
    > Rather, over in UI we have:
    >
    > -(void)windowWillClose:(NSNotification *)aNotification
    > {
    > [self autorelease];
    > }
    >
    > That is: after UI has done all it needs to do, and the user clicks
    > the final button (connected here), it autoreleases itself,
    > completes, and the pool finishes it off.
    >
    > This is all working; nothing leaks, nothing is prematurely
    > dealloc'ed.  But having the alloc and (auto)release in separate
    > classes seems unsound.

    It works but ugly. This is not the way memory is managed usually,
    which will cause any new developer to waste time on this code.

    >
    > I found an earlier thread in this list,
    >
    > http://lists.apple.com/archives/cocoa-dev/2002/Jul/msg01001.html
    >
    > which is darned close to my situation (almost, but not quite,
    > totally like).  This thread solves its very similar problem by
    > having Controller keep a ref to UI, and release it during
    > Controller's dealloc.  I can't quite see how to handle that in my
    > case.  A minor problem is that I may have an unpredictable number
    > of these UI objects about, so I'd need an NSMutableArray of them or
    > something, not just a scalar field.  But a bigger problem is that
    > Controller isn't dealloc'ed until the end of the program, which is
    > far too late: it's a long-lived, server-like program, I'd be
    > accumulating these UI objects forever.

    You may want to keep the active UI object in a collection and remote
    them when they are done.

    >
    > - Is my eternal, shared Controller bogus, and I should be finding
    > some way to create multiple Controllers, and destroy them more often?

    There is nothing bogus in controlling multiple objects. If the
    controller is simple and easy to maintain and change, it is probably
    fine.

    >
    > - Is there some way to get the windowWillClose event passed back
    > to Controller, rather than to UI, so the autorelease can happen there?

    The simplest solution would be to add a delegate to the UI. The
    controller will set itself as the delegate when creating a UI object,
    and the UI will notify the delegate when its done.

    If there are other objects that may be interested in the UI
    completion event, you may like to use notifications instead. The
    controller will observer UI notifications and the UI will post
    notification when its done.

    Best Regards,

    Nir Soffer
  • > FWIW, I wouldn't call it wrong for an object to manage its own
    > lifetime. You'll leak UIs if the notification never appears, but
    > this is apparently not a problem.

    This would be a problem should the code base ever be compiled with GC
    support. Granted that is unlikely as this is an existing code base
    using retain/release logic but I feel that compiling with GC enabled
    helps to expose poor design decisions, i.e. well designed code should
    be able to be compiled GC supported without any changes. I'm not one
    of the lucky few who can claim to have a perfectly designed code base,
    even my unreleased Leopard transitioned app required some changes.

    Back on topic I would agree with the OP, this way of doing things it
    Not Good.

    I'm a purist, so I feel that the net retain count in any block should
    be zero (there are exceptions to every rule, I guess I'm not that much
    of a purist!). I'd solve this by placing the UI object in a mutable
    set when creating it. I'd then release it - remove it from the set -
    in a delegate callback. This is a much more sustainable and future
    proof pattern, should the controller need to do any work when the UI
    object returns, it can.

    Keith
  • On Feb 22, 2008, at 1:13 PM, Keith Duncan wrote:
    > This would be a problem should the code base ever be compiled with
    > GC support.

    Interesting point, that retain/release supports a lifecycle model
    (free-floating, self-managed object) that can't be supported by GC....

    -==-
    Jack Repenning
    Chief Technology Officer
    CollabNet, Inc.
    8000 Marina Boulevard, Suite 600
    Brisbane, California 94005
    office: +1 650.228.2562
    mobile: +1 408.835.8090
    raindance: +1 877.326.2337, x844.7461
    aim: jackrepenning
    skype: jrepenning
  • --- Jack Repenning <jrepenning...> wrote:

    > On Feb 22, 2008, at 1:13 PM, Keith Duncan wrote:
    >> This would be a problem should the code base ever
    > be compiled with
    >> GC support.
    >
    > Interesting point, that retain/release supports a
    > lifecycle model
    > (free-floating, self-managed object) that can't be
    > supported by GC....

    I think it can. You'd just need to disable collection
    on the object and have it re-enable collection for
    itself when it decided it was no longer needed (this
    is essentially what CFRetain and CFRelease do under
    GC).

    Cheers,
    Chuck

          ____________________________________________________________________________________
    Be a better friend, newshound, and
    know-it-all with Yahoo! Mobile.  Try it now.  http://mobile.yahoo.com/;_ylt=Ahu06i62sR8HDtDypao8Wcj9tAcJ
  • On Feb 22, 2008, at 5:17 PM, Charles Steinman wrote:

    >
    > --- Jack Repenning <jrepenning...> wrote:
    >
    >> On Feb 22, 2008, at 1:13 PM, Keith Duncan wrote:
    >>> This would be a problem should the code base ever
    >> be compiled with
    >>> GC support.
    >>
    >> Interesting point, that retain/release supports a
    >> lifecycle model
    >> (free-floating, self-managed object) that can't be
    >> supported by GC....
    >
    > I think it can. You'd just need to disable collection
    > on the object and have it re-enable collection for
    > itself when it decided it was no longer needed (this
    > is essentially what CFRetain and CFRelease do under
    > GC).

    Where is this magic documented?  The documentation for CFRetain
    doesn't mention this usage.  The way I have seen documented is to use
    NSGarbageCollector's disableCollectorForPointer: and
    enableCollectorForPointer: methods.  So you'd say:

    [[NSGarbageCollector defaultCollector] disableCollectorForPointer:ui];

    to cause ui to be ignored by the GC, and then

    [[NSGarbageCollector defaultCollector] enableCollectorForPointer:ui];

    when you want the GC to take over again.  You're saying that CFRetain
    and CFRelease do the same thing, even for any Cocoa object?
  • On Feb 23, 2008, at 12:10 AM, Adam P Jenkins wrote:

    > Where is this magic documented?  The documentation for CFRetain
    > doesn't mention this usage.

    See: <http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection
    /Introduction.html
    >

    Using CFRetain on an object doesn't quite "disable collection" for the
    object, but rather bumps its retain count (Yes, RC in GC!). A retain
    count > 0 will prevent collection - even if there are no other
    references to this object that would otherwise keep it alive.

    j o a r
  • Interesting discussion, thanks.  Not surprising, I suppose, if there's
    some split on a "matter of taste" like whether it's legitimate to have
    an object manage its own lifecycle ([self autorelease]).  FWIW, I
    chose Quincey's solution, [[alloc]init]autorelease] in Controller, and
    [[super init]retain] plus [self autorelease] in UI.  This keeps the
    number of lines you have to scan to verify retain/release balance to a
    minimum, anyway.

    GC compatibility can wait until I have more time to think about the
    new GC.  Since I'm still supporting Panther clients, this may be a
    long time....

    -==-
    Jack Repenning
    Chief Technology Officer
    CollabNet, Inc.
    8000 Marina Boulevard, Suite 600
    Brisbane, California 94005
    office: +1 650.228.2562
    mobile: +1 408.835.8090
    raindance: +1 877.326.2337, x844.7461
    aim: jackrepenning
    skype: jrepenning
previous month february 2008 next month
MTWTFSS
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29    
Go to today