Bindings cause leak?

  • I've started looking at my app with OmniObjectMeter, and I think I
    discovered a big leak (running in 10.4.8).

    I have sheets in separate nib files, and load them with

    if (fInfoSheet == nil) {
      [NSBundle loadNibNamed: @"Info" owner: self];
      // fInfoSheet is a connection in File’s Owner
    }

    The sheet window has a number of bindings on File's Owner. OOM
    reports 9 extra retains, which looks like the number of bindings.

    File's Owner is an NSView subclass, so <http://
    theobroma.treehouseideas.com/document.page/18> would appear not to
    apply.

    How can my view shed its bindings so it can be dealloced?

    The above is a pattern I use frequently, and I'd really hate to have
    to set up outlets so I can release anyone who binds to my view, as
    one person hinted at.

    David Dunham    A Sharp, LLC
    Voice/Fax: 206 783 7404    http://a-sharp.com
    Efficiency is intelligent laziness.
  • This is a known bugs. It was partly fixed in Tiger, but only partly (in
    particular when the owner is an NSWindowController or an NSDocument, but
    then not even always). So whenever the owner is not an NSWindowController
    or NSDocument, binding to the file owner leads to a leak, as the binding
    retains the target (and the owner retains everything in the nib). I've no
    idea how this was fixed in NSWindowController.

    The way to fix this is by binding instead to an NSObjectController in the
    nib. Then after you load the nib you have to set the contentObject of the
    objectController to the owner object. And you have to make sure this
    connection is severed when the owner is supposed to be disposed (but not
    in -dealloc, as that won't be called before the connection is severed).
    Typically you do this by observing a window closing notification or
    delegate method. Or, if your owner object manages a subview, when the
    subview is removed.

    HTH,
    Christiaan

    > I've started looking at my app with OmniObjectMeter, and I think I
    > discovered a big leak (running in 10.4.8).
    >
    > I have sheets in separate nib files, and load them with
    >
    > if (fInfoSheet == nil) {
    > [NSBundle loadNibNamed: @"Info" owner: self];
    > // fInfoSheet is a connection in File’s Owner
    > }
    >
    > The sheet window has a number of bindings on File's Owner. OOM
    > reports 9 extra retains, which looks like the number of bindings.
    >
    > File's Owner is an NSView subclass, so <http://
    > theobroma.treehouseideas.com/document.page/18> would appear not to
    > apply.
    >
    > How can my view shed its bindings so it can be dealloced?
    >
    > The above is a pattern I use frequently, and I'd really hate to have
    > to set up outlets so I can release anyone who binds to my view, as
    > one person hinted at.
    >
  • On 24 Oct 2006, at 04:21, Christiaan Hofman wrote:

    > The way to fix this is by binding instead to an NSObjectController
    > in the
    > nib.

    Any way to do this in bulk? I noticed for one of my sheets that there
    were 29 extra references, so this is 29 bindings I would have to change.

    David Dunham    A Sharp, LLC
    Voice/Fax: 206 783 7404    http://a-sharp.com
    "People seem to misinterpret complexity as sophistication" -- Niklaus
    Wirth
  • On 24 Oct 2006, at 04:21, Christiaan Hofman wrote:

    > The way to fix this is by binding instead to an NSObjectController
    > in the
    > nib. Then after you load the nib you have to set the contentObject
    > of the
    > objectController to the owner object. And you have to make sure this
    > connection is severed when the owner is supposed to be disposed
    > (but not
    > in -dealloc, as that won't be called before the connection is
    > severed).
    > Typically you do this by observing a window closing notification or
    > delegate method. Or, if your owner object manages a subview, when the
    > subview is removed.

    Using NSObjectController reduces the number of extra retains, but
    only to a point. Apparently this is *another* known bug (http://
    www.borkware.com/quickies/one?topic=Bindings). I put in this horrible
    hack into my NSView

    - (void)windowWillClose:(NSNotification*)aNotification
    {
    if (fInfoController != nil) {
      [fInfoController setContent: nil];
      [self release];  // !!! hack
      [self release];  // !!! hack
    }
    }

    which does the trick, but seems icky.

    Does Cocoa really have memory leaks (due to reference loops) like
    this? Bindings aren't new technology any more.

    (I have some thoughts on how to make my hack slightly less evil, but
    I hope there's a better workaround altogether.)

    David Dunham    A Sharp, LLC
    Voice/Fax: 206 783 7404    http://a-sharp.com
    Efficiency is intelligent laziness.
  • On Oct 24, 2006, at 11:15 PM, David Dunham wrote:

    > Using NSObjectController reduces the number of extra retains, but
    > only to a point. Apparently this is *another* known bug (http://
    > www.borkware.com/quickies/one?topic=Bindings). I put in this
    > horrible hack into my NSView

    Just to ask an obvious question, are we sure this is a true memory
    leak problem?

    A high retain count (obviously) isn't a problem in itself, only if it
    doesn't go down when it should. I can't tell from the original post
    if that's something that has been tested.

        - Scott
  • > On 24 Oct 2006, at 04:21, Christiaan Hofman wrote:
    >
    >> The way to fix this is by binding instead to an NSObjectController
    >> in the
    >> nib. Then after you load the nib you have to set the contentObject
    >> of the
    >> objectController to the owner object. And you have to make sure this
    >> connection is severed when the owner is supposed to be disposed
    >> (but not
    >> in -dealloc, as that won't be called before the connection is
    >> severed).
    >> Typically you do this by observing a window closing notification or
    >> delegate method. Or, if your owner object manages a subview, when the
    >> subview is removed.
    >
    > Using NSObjectController reduces the number of extra retains, but
    > only to a point. Apparently this is *another* known bug (http://
    > www.borkware.com/quickies/one?topic=Bindings). I put in this horrible
    > hack into my NSView
    >
    > - (void)windowWillClose:(NSNotification*)aNotification
    > {
    > if (fInfoController != nil) {
    > [fInfoController setContent: nil];
    > [self release];        // !!! hack
    > [self release];        // !!! hack
    > }
    > }
    >
    > which does the trick, but seems icky.
    >
    > Does Cocoa really have memory leaks (due to reference loops) like
    > this? Bindings aren't new technology any more.
    >
    > (I have some thoughts on how to make my hack slightly less evil, but
    > I hope there's a better workaround altogether.)
    >
    > David Dunham    A Sharp, LLC
    > Voice/Fax: 206 783 7404    http://a-sharp.com
    > Efficiency is intelligent laziness.
    >

    This sounds very dangerous. I have never seen the fix not working, so if I
    would do this in my code it would lead to a crash. Don't do this if you're
    not knowing what you are doing. And when Apple would come with a fix, this
    will give a crash in the future. Unbind messages are much safer (but could
    be more work). However I don't see what bug this could be. The link you
    gave also doesn't work. Can you explain to me what this other bug is? Are
    you sure your object is not dealloc-ed when you don't send the 2 release
    messages? At the point of windowWillClose there still are several retains
    on the view, for sure, even without the bug.

    Christiaan
  • On 25 Oct 2006, at 01:25, Scott Stevenson wrote:

    >> Using NSObjectController reduces the number of extra retains, but
    >> only to a point. Apparently this is *another* known bug (http://
    >> www.borkware.com/quickies/one?topic=Bindings). I put in this
    >> horrible hack into my NSView
    >
    > Just to ask an obvious question, are we sure this is a true memory
    > leak problem?
    >
    > A high retain count (obviously) isn't a problem in itself, only if
    > it doesn't go down when it should. I can't tell from the original
    > post if that's something that has been tested.

    A non-zero retain count after my window is closed is a leak. (This
    one wasn't picked up by the "leaks" tool because it's apparently a
    retain cycle.)

    David Dunham    A Sharp, LLC
    Voice/Fax: 206 783 7404    http://a-sharp.com
    Efficiency is intelligent laziness.
  • On 25 Oct 2006, at 02:34, Christiaan Hofman wrote:

    > This sounds very dangerous. I have never seen the fix not working,
    > so if I
    > would do this in my code it would lead to a crash. Don't do this if
    > you're
    > not knowing what you are doing.

    I'm certainly not going to use the simplified form I posted -- it's
    obviously skanky and could break in the future.

    > Unbind messages are much safer (but could be more work).

    30 bindings in one sheet... (I may still go this route, but the whole
    thing sucks.)

    > Can you explain to me what this other bug is?

    That somebody (presumably the NSObjectController) is still holding on
    to my NSView, even if I bind to the NSObjectController instead of
    directly to my NSView.

    (If I never open the sheet, my object is released down to zero when
    the window closes.)

    > Are you sure your object is not dealloc-ed when you don't send the
    > 2 release
    > messages?

    Yes, I'm sure. I've been testing with the debugger and via
    OmniObjectMeter.

    > At the point of windowWillClose there still are several retains
    > on the view, for sure, even without the bug.

    I think it's 2 (if I don't bind), and they do eventually go away.

    The borkware site seems to be down -- I used the Google cache. But
    Jim wrote:

    Fixing Bindings-related memory leak
    If you have bindings hooked up to File's Owner in some circumstances
    (like with an NSWindowController-based nib file), a retain cycle is
    created, and so the window controller will never get released (having
    an extra retain for each binding to File's Owner). An easy way to
    work around this is to;
    Add an NSObjectController (I call it "Fake FIle's Owner") to the nib
    file and the window controller class.
    Point the bindings to the controller rather than file's owner
    Hook up the window controller's outlet to point to the object
    controller
    [fakeFileOwner setContent: self]
    Then again, this all might be Wrong. jkp_ in #macdev points out that
    the setContent:self will retain self twice more, with no release in
    sight. The only wayd that would really work is if you provided a
    subclass of NSObjectController that released after it setContent: and
    also in _startObservingObject: That might be the cleanest solution -
    provide a custom proxy object that doesnt retain the filesOwner and
    bind to that....same thing, only you have to do the overrides. So
    this one's in limbo(e) for now. wheeee! He's come up with a hack to
    work around things:

    - (oneway void) release;
    {
        // special case when count is 3, we are being retained twice by
    the object controller...
        if ( [self retainCount] == 3 )
        {
            [super release];
            [filesOwnerProxy setContent:nil];
            return;
        }

        [super release];
    }

    David Dunham    A Sharp, LLC
    Voice/Fax: 206 783 7404    http://a-sharp.com
    "People seem to misinterpret complexity as sophistication" -- Niklaus
    Wirth
  • On 25 Oct 2006, at 02:34, Christiaan Hofman wrote:

    > Unbind messages are much safer (but could
    > be more work).

    They're not reducing my final retainCount, and looking at the docs,
    they probably aren't the right approach. It looks like they'd need to
    be sent to the object that's binding to my NSView.

    David Dunham    A Sharp, LLC
    Voice/Fax: 206 783 7404    http://a-sharp.com
    Efficiency is intelligent laziness.
  • On 25 Oct 2006, at 02:34, Christiaan Hofman wrote:

    > This sounds very dangerous. I have never seen the fix not working,
    > so if I
    > would do this in my code it would lead to a crash.

    I figured this out -- I had a couple of bindings I'd missed
    converting from File's Owner to NSObjectController. (I hate IB's
    inability to show bindings.) So I don't need my hack.

    I don't know why this hack is mentioned elsewhere then.

    David Dunham    A Sharp, LLC
    Voice/Fax: 206 783 7404    http://a-sharp.com
    "People seem to misinterpret complexity as sophistication" -- Niklaus
    Wirth
  • On Thu, 26 Oct 2006 23:28:06 -0700, David Dunham <dunham...> said:
    >
    > (I hate IB's
    > inability to show bindings.)

    It does better at this than most people think, though; in Instances view,
    press the second little icon in the upper right (looks like parallel lines,
    rather than boxes) to view everything listed hierarchically along with
    connections. Those connections include bindings. Click a grey triangle to
    reveal all those connections. The graphical info is actually quite
    informative.

    The other option is to use nibtool, of course. Keith Wilson started a
    project to parse nibtool's output in a somewhat more convenient form:

    <http://nibreporter.berlios.de/>

    The project seems to have fallen into abeyance, however.

    m.

    --
    matt neuburg, phd = <matt...>, <http://www.tidbits.com/matt/>
    A fool + a tool + an autorelease pool = cool!
    AppleScript: the Definitive Guide - Second Edition!
    <http://www.amazon.com/gp/product/0596102119>
  • On 27 Oct 2006, at 18:49, Matt Neuburg wrote:

    >> (I hate IB's inability to show bindings.)
    >
    > It does better at this than most people think

    Including me. I didn't realize the hierarchical view included
    bindings. IB, I apologize. Matt, thanks.

    > The other option is to use nibtool, of course.

    I tried this and couldn't see anything that looked like a binding.

    David Dunham    A Sharp, LLC
    Voice/Fax: 206 783 7404    http://a-sharp.com
    Efficiency is intelligent laziness.
  • On Fri, 27 Oct 2006 19:12:29 -0700, David Dunham <dunham...> said:
    > On 27 Oct 2006, at 18:49, Matt Neuburg wrote:
    >
    >>> (I hate IB's inability to show bindings.)
    >>
    >> It does better at this than most people think
    >
    > Including me. I didn't realize the hierarchical view included
    > bindings. IB, I apologize. Matt, thanks.
    >
    >> The other option is to use nibtool, of course.
    >
    > I tried this and couldn't see anything that looked like a binding.

    Then you tried it wrong. :) Bindings are most definitely listed. Watch
    closely, and notice that my fingers do not for a moment leave my hands:

    $ nibtool -a MainMenu.nib

    [snip]

        "Connection 613" = {
            Binding = "value";
            Class = "NSNibBindingConnector";
            Controller = "431";
            KeyPath = "values.showFileSizes";
            Label = "value: values.showFileSizes";
            Object = "609";
        };

    [snip]

    But do also give NibReporter a try. It parses the nibtool output and
    assembles the related information (for example, in the above, what's "431"
    and "609"?). If you were to improve it, that would be even better. Of
    course, what would *really* help would be if nibtool's output were valid
    plist format. m.

    --
    matt neuburg, phd = <matt...>, <http://www.tidbits.com/matt/>
    A fool + a tool + an autorelease pool = cool!
    AppleScript: the Definitive Guide - Second Edition!
    <http://www.amazon.com/gp/product/0596102119>
previous month october 2006 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
30 31          
Go to today