NSAutounbinder not releasing my object in a timely manner

  • I am tearing my hair out here with yet another retain issue with the NSAutounbinder. I don't know what I am doing to keep hitting these problems, and unfortunately going back through peoples past (very helpful) replies to my questions on both this and blocks+autorelease pools has not shed any light on things for me.

    The situation I have is I have a sheet on a window, with the sheet containing a progress indicator (and time estimate). This has a window controller associated with it, called CocoaProgressWindow (which is also the window delegate). I release the window controller, and soon afterwards its retain count falls to zero (as it should). However, this nasty NSAutounbinder thingy then jumps in and re-retains it in some evil under-the-covers voodoo to do with avoiding retain cycles (as I understand it). Unfortunately the balancing autorelease only occurs 16 seconds later when I move the mouse! While not catastrophic this is pretty annoying and looks rather embarrassing.

    Can anybody suggest what might be happening here? I can only assume I am doing something wrong somewhere along the line, although I cannot imagine what it might be! I have recorded full callstacks of every time my retain/release is called:
    http://www.dur.ac.uk/j.m.taylor/complete-call-stack.txt
    a version editing out the really obvious, balanced retain/releases that I know about:
    http://www.dur.ac.uk/j.m.taylor/call-stack-shortened.txt
    and one edited down to just show the two dodgy callstacks at the end:
    http://www.dur.ac.uk/j.m.taylor/call-stack-shortened-2.txt

    In general terms, I am running some demanding work (generating a movie file) on a GCD serial thread using dispatch_async and there is then a second nested dispatch_async back onto the main queue, from which I release the window controller. It is when that block is destroyed that the final "real" release occurs and the NSAutounbinder stuff happens. This is under Snow Leopard, no GC or ARC. It might be relevant that I am running with Mike Ash's ZeroingWeakReference code compiled in, although the code in question here makes no use of that facility whatsoever.

    Can anybody speculate what my problem might be, and how I can deal with it?
    Many thanks
    Jonny
  • On 2012 Jul 18, at 09:45, Jonathan Taylor wrote:

    > this nasty NSAutounbinder thingy then jumps in and re-retains it in some evil under-the-covers voodoo to do with avoiding retain cycles (as I understand it). Unfortunately the balancing autorelease only occurs 16 seconds later when I move the mouse! While not catastrophic this is pretty annoying and looks rather embarrassing.

    It's not embarrassing unless your users are running your app in a debugger.  Cocoa performs millions of unnecessary operations every second, and indeed any of them could cause trouble.  But only Apple can fix them.  As long as it doesn't crash, and doesn't leak, I wouldn't worry about it.
  • >> this nasty NSAutounbinder thingy then jumps in and re-retains it in some evil under-the-covers voodoo to do with avoiding retain cycles (as I understand it). Unfortunately the balancing autorelease only occurs 16 seconds later when I move the mouse! While not catastrophic this is pretty annoying and looks rather embarrassing.

    > It's not embarrassing unless your users are running your app in a debugger.

    Sorry, you've lost me there. Are you saying this will not occur if I am running standalone rather than under XCode? (Unfortunately I don't have the code in front of me to try this out). If that is the case then that's good - if frustrating - news. Is this a known issue then? I was working on the assumption that I was doing something obscurely wrong, or at least non-standard somewhere to trigger this from happening.

    Or are you saying you only see the NSAutounbinder stuff in the debugger? If so then - sure, but it's the fact that the progress bar is just sitting there until I move the mouse that alarms me.
  • On Jul 18, 2012, at 3:28 PM, TAYLOR J.M. wrote:

    >>> this nasty NSAutounbinder thingy then jumps in and re-retains it in some evil under-the-covers voodoo to do with avoiding retain cycles (as I understand it). Unfortunately the balancing autorelease only occurs 16 seconds later when I move the mouse! While not catastrophic this is pretty annoying and looks rather embarrassing.
    >
    >> It's not embarrassing unless your users are running your app in a debugger.
    >
    > Sorry, you've lost me there. Are you saying this will not occur if I am running standalone rather than under XCode? (Unfortunately I don't have the code in front of me to try this out). If that is the case then that's good - if frustrating - news. Is this a known issue then? I was working on the assumption that I was doing something obscurely wrong, or at least non-standard somewhere to trigger this from happening.
    >
    > Or are you saying you only see the NSAutounbinder stuff in the debugger? If so then - sure, but it's the fact that the progress bar is just sitting there until I move the mouse that alarms me.

    Are you saying you're relying on an object being deallocated to change your GUI?  You shouldn't be.  If you want to close or hide a window, send it a -close or -orderOut: message.  If you want to remove or hide a view, remove it from the view hierarchy or send it -setHidden:YES.  If its hidden binding is bound to a model or controller property, set that property (on the main thread).

    Regards,
    Ken
  • On 2012 Jul 18, at 13:28, TAYLOR J.M. wrote:

    > it's the fact that the progress bar is just sitting there until I move the mouse that alarms me.

    I missed that part.

    So, this progress bar is in a sheet.  Is the sheet done?  If so, -endSheet:.  If not, send -setHidden:YES to the progress bar.

    Your problem is to remove the progress bar from the screen.  Do not worry about when it's being autoreleased and deallocced.

    I have to go now but maybe someone else will give it some more intelligent thought.
  • >>>> this nasty NSAutounbinder thingy then jumps in and re-retains it in some evil under-the-covers voodoo to do with avoiding retain cycles (as I understand it). Unfortunately the balancing autorelease only occurs 16 seconds later when I move the mouse! While not catastrophic this is pretty annoying and looks rather embarrassing.
    >>
    >>> It's not embarrassing unless your users are running your app in a debugger.
    >>
    >> Sorry, you've lost me there. Are you saying this will not occur if I am running standalone rather than under XCode? (Unfortunately I don't have the code in front of me to try this out). If that is the case then that's good - if frustrating - news. Is this a known issue then? I was working on the assumption that I was doing something obscurely wrong, or at least non-standard somewhere to trigger this from happening.
    >>
    >> Or are you saying you only see the NSAutounbinder stuff in the debugger? If so then - sure, but it's the fact that the progress bar is just sitting there until I move the mouse that alarms me.
    >
    > Are you saying you're relying on an object being deallocated to change your GUI?  You shouldn't be.  If you want to close or hide a window, send it a -close or -orderOut: message.  If you want to remove or hide a view, remove it from the view hierarchy or send it -setHidden:YES.  If its hidden binding is bound to a model or controller property, set that property (on the main thread).

    Right, that makes a lot of sense. I can understand the principle of not relying on the dealloc for that sort of thing and on first reading that made complete sense. However, I have now tracked down/remembered the piece of code that was my motivation for taking the approach I did. As always, it turns out the real question is probably not quite the one I originally asked! Can I get your opinion on this workflow? This is how I had previously done things:

    - User requests batch processing of data within a folder tree, which will result in between 0 and many separate movies being generated.
    - Application allocates a "progress" tracker, subclassed from NSWindowController, in control of a window containing a progress indicator and various other feedback elements.
    - This is supplied to separate traversal/parsing code which identifies source data from which movies should be generated.
    - For each movie to be generated, the progress indicator is retained in association with a GCD block which will be executed to perform the actual work, added to a serial queue
    - Following tree traversal, the application releases its reservation on the progress tracker. If no movies are due to be generated, I had intended this to cause it to be deallocated, at which point I had been closing the window.

    If work was queued, this will in due course be executed. The current work block will take control of updating the progress tracker. When it is finished it will release its reservation of the progress tracker. Again, when all work has been completed I intended the retain count to fall to zero at which point it would be deallocated and closed.

    My intention with all this was to avoid the need for any "information sharing" between bits of code that I felt should be fairly independent. The application-level code that originally allocates the progress tracker doesn't need to care about what any more specific code wants to do with the progress tracker, it just makes it available to anything that wants to retain it for future use, and then relinquishes any further "personal" interest in that object.

    Now, your statement that "[you shouldn't be] relying on an object being deallocated to change your GUI" seems fair, but how would you tackle my scenario here? One possible solution would be to have an intermediary object with no gui/binding/etc of its own, which allocates and owns the progress window and is retained and released by my code as necessary, closing/releasing the window when it is deallocated. In practice, I suspect that would work because that intermediary object "shouldn't" get caught up in any of the deferred autoreleasing that was associated with my original problem. However I am genuinely not sure whether that is just a slightly more indirect way of violating your advice, or whether it is acceptable. I guess I am not sure whether your statement is a generic design principle or a practical rule of thumb for avoiding the sort of issue I encountered.

    In my mind retain/release is a way of tracking the interest that my different work packages have on the progress window. If you say you don't like my intermediary object approach then my next proposal will be that I implement a "manual" counter of the number of work packages outstanding... which is really just me reimplementing a parallel retain/release mechanism myself. Is *that* more acceptable, or am I again violating the spirit if not the letter of your statement?

    I've rambled a bit here, in my attempts to understand the thinking behind your helpful advice. Would you mind outlining how you would approach this situation yourself?

    Many thanks for your comments
    Jonny.
  • Jonathan,
    Could you not watch the serial queue you mentioned? When it is empty, release the progress window.

    - Erik

    Sent from my iPad

    On 2012-07-19, at 4:02 AM, Jonathan Taylor <j.m.taylor...> wrote:

    >>>>> this nasty NSAutounbinder thingy then jumps in and re-retains it in some evil under-the-covers voodoo to do with avoiding retain cycles (as I understand it). Unfortunately the balancing autorelease only occurs 16 seconds later when I move the mouse! While not catastrophic this is pretty annoying and looks rather embarrassing.
    >>>
    >>>> It's not embarrassing unless your users are running your app in a debugger.
    >>>
    >>> Sorry, you've lost me there. Are you saying this will not occur if I am running standalone rather than under XCode? (Unfortunately I don't have the code in front of me to try this out). If that is the case then that's good - if frustrating - news. Is this a known issue then? I was working on the assumption that I was doing something obscurely wrong, or at least non-standard somewhere to trigger this from happening.
    >>>
    >>> Or are you saying you only see the NSAutounbinder stuff in the debugger? If so then - sure, but it's the fact that the progress bar is just sitting there until I move the mouse that alarms me.
    >>
    >> Are you saying you're relying on an object being deallocated to change your GUI?  You shouldn't be.  If you want to close or hide a window, send it a -close or -orderOut: message.  If you want to remove or hide a view, remove it from the view hierarchy or send it -setHidden:YES.  If its hidden binding is bound to a model or controller property, set that property (on the main thread).
    >
    > Right, that makes a lot of sense. I can understand the principle of not relying on the dealloc for that sort of thing and on first reading that made complete sense. However, I have now tracked down/remembered the piece of code that was my motivation for taking the approach I did. As always, it turns out the real question is probably not quite the one I originally asked! Can I get your opinion on this workflow? This is how I had previously done things:
    >
    > - User requests batch processing of data within a folder tree, which will result in between 0 and many separate movies being generated.
    > - Application allocates a "progress" tracker, subclassed from NSWindowController, in control of a window containing a progress indicator and various other feedback elements.
    > - This is supplied to separate traversal/parsing code which identifies source data from which movies should be generated.
    > - For each movie to be generated, the progress indicator is retained in association with a GCD block which will be executed to perform the actual work, added to a serial queue
    > - Following tree traversal, the application releases its reservation on the progress tracker. If no movies are due to be generated, I had intended this to cause it to be deallocated, at which point I had been closing the window.
    >
    > If work was queued, this will in due course be executed. The current work block will take control of updating the progress tracker. When it is finished it will release its reservation of the progress tracker. Again, when all work has been completed I intended the retain count to fall to zero at which point it would be deallocated and closed.
    >
    > My intention with all this was to avoid the need for any "information sharing" between bits of code that I felt should be fairly independent. The application-level code that originally allocates the progress tracker doesn't need to care about what any more specific code wants to do with the progress tracker, it just makes it available to anything that wants to retain it for future use, and then relinquishes any further "personal" interest in that object.
    >
    > Now, your statement that "[you shouldn't be] relying on an object being deallocated to change your GUI" seems fair, but how would you tackle my scenario here? One possible solution would be to have an intermediary object with no gui/binding/etc of its own, which allocates and owns the progress window and is retained and released by my code as necessary, closing/releasing the window when it is deallocated. In practice, I suspect that would work because that intermediary object "shouldn't" get caught up in any of the deferred autoreleasing that was associated with my original problem. However I am genuinely not sure whether that is just a slightly more indirect way of violating your advice, or whether it is acceptable. I guess I am not sure whether your statement is a generic design principle or a practical rule of thumb for avoiding the sort of issue I encountered.
    >
    > In my mind retain/release is a way of tracking the interest that my different work packages have on the progress window. If you say you don't like my intermediary object approach then my next proposal will be that I implement a "manual" counter of the number of work packages outstanding... which is really just me reimplementing a parallel retain/release mechanism myself. Is *that* more acceptable, or am I again violating the spirit if not the letter of your statement?
    >
    > I've rambled a bit here, in my attempts to understand the thinking behind your helpful advice. Would you mind outlining how you would approach this situation yourself?
    >
    > Many thanks for your comments
    > Jonny.
  • On Jul 19, 2012, at 6:02 AM, Jonathan Taylor wrote:

    > - User requests batch processing of data within a folder tree, which will result in between 0 and many separate movies being generated.
    > - Application allocates a "progress" tracker, subclassed from NSWindowController, in control of a window containing a progress indicator and various other feedback elements.
    > - This is supplied to separate traversal/parsing code which identifies source data from which movies should be generated.
    > - For each movie to be generated, the progress indicator is retained in association with a GCD block which will be executed to perform the actual work, added to a serial queue
    > - Following tree traversal, the application releases its reservation on the progress tracker. If no movies are due to be generated, I had intended this to cause it to be deallocated, at which point I had been closing the window.
    >
    > If work was queued, this will in due course be executed. The current work block will take control of updating the progress tracker. When it is finished it will release its reservation of the progress tracker. Again, when all work has been completed I intended the retain count to fall to zero at which point it would be deallocated and closed.

    > Now, your statement that "[you shouldn't be] relying on an object being deallocated to change your GUI" seems fair, but how would you tackle my scenario here?

    This sounds like a job for a dispatch group.  Schedule all of the blocks using one of the dispatch_group_[a]sync() functions.  Set up a block to be performed when they are all completed using dispatch_group_notify().

    > One possible solution would be to have an intermediary object with no gui/binding/etc of its own, which allocates and owns the progress window and is retained and released by my code as necessary, closing/releasing the window when it is deallocated. In practice, I suspect that would work because that intermediary object "shouldn't" get caught up in any of the deferred autoreleasing that was associated with my original problem.

    I would expect it to have exactly the same problem.

    > However I am genuinely not sure whether that is just a slightly more indirect way of violating your advice, or whether it is acceptable. I guess I am not sure whether your statement is a generic design principle or a practical rule of thumb for avoiding the sort of issue I encountered.

    I would say it's a general principle.  Deallocation is for releasing resources.  Don't predicate any behavior of your program which is not related specifically to resource management on deallocation.  Ideally, you wouldn't even wait for deallocation to release resources.  It's been Apple's advice for a while, for example, to explicitly close a file handle when the code which owns the file handle knows it's done with it.  Don't just leave it to dealloc.

    > In my mind retain/release is a way of tracking the interest that my different work packages have on the progress window.

    No, it's a way of tracking ownership of the resource, which is slightly different.  Of course an object should keep ownership of the progress window for as long as it "has an interest" in it, but that's not what it truly represents.  You're currently dealing with the case where the two diverge.

    > If you say you don't like my intermediary object approach then my next proposal will be that I implement a "manual" counter of the number of work packages outstanding... which is really just me reimplementing a parallel retain/release mechanism myself. Is *that* more acceptable, or am I again violating the spirit if not the letter of your statement?

    I don't consider myself any sort of arbiter of these things, so "acceptable" isn't the right word, but I don't have any problem with that.  First, your mechanism would presumably not have anything like autorelease.  Second, since it's not a mechanism that the rest of Cocoa will participate in, you don't get other code implicitly and/or unwittingly changing the behavior of your code.  Finally, it won't be a mechanism with another, unrelated semantic purpose that you'd be overloading.

    Regards,
    Ken
  • Thankyou both for your very helpful replies. The dispatch group approach sounds like a nice one - especially using dispatch_group_notify, which I had somehow overlooked. Thanks also Ken for your detailed reply to my ramblings, that was very useful for getting my understanding straightened out.
    Jonny.
previous month july 2012 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