Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
274 views
in Technique[技术] by (71.8m points)

objective c - Should I need to unbind cocoa-bindings in dealloc of windowController?

I have window controller and view controller that use core data bindings, but I want to be able to have those views really, truly get deallocated. I then want to reset the managedObjectContext and at that point have given up as much memory as possible.

I have found that I am required to unbind things which I've bound in the Nib, or the MOC keeps the Nib objects retained.

The issue is this EXEC_BAD_ACCESS trace:

enter image description here

If I do not unbind all of the bindings, even those created in Interface Builder, reset on the MOC causes an EXEC_BAD_ACCESS because bindings are attempting to reflect changes in the MOC in the view that's gone, through an array controller that should be gone, but isn't.

So I did this in the window controller's dealloc:

- (void) dealloc 
{
    NSLog(@"Wincon dealloc");
    @autoreleasepool {
        // Remove subview to ensure subview dealloc
        [_viewController.view removeFromSuperviewWithoutNeedingDisplay];

        // Tear down bindings to ensure MOC can reset
        for (NSObject<NSKeyValueBindingCreation>* object in
             @[_watcherAC, _watchersTimesTreeController, _watcherTableView, _itemsOutlineView])
        {
            for (NSString* binding in [object exposedBindings])
                [object unbind:binding];
        }
    }
}

which is triggered this way:

- (void) switchToBackgroundMode
{
    NSLog(@"SwitchToBackgroundMode");

    // Hide the menu and dock icon
    [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];

    // Force every view to deallocate before reset
    @autoreleasepool {
        // Need to check loaded to prevent closing a closed window and
        //  triggering a second call to applicationShouldTerminateAfterLastWindowClosed
        if ([self.wincon isWindowLoaded]) [self.wincon close];
        self.wincon = nil;
    }

    NSLog(@"About to resetCoreDataStack");
    [self resetCoreDataStack];
}

... and now I don't get any errors with that resetCoreDataStack

The stack trace above comes with a log file like this:

2014-05-29 15:54:35.794 MyApp[10230:303] Switch to BG in appShouldTerminate
2014-05-29 15:54:35.794 MyApp[10230:303] SwitchToBackgroundMode
2014-05-29 15:54:35.808 MyApp[10230:303] Wincon dealloc
2014-05-29 15:54:35.830 MyApp[10230:303] About to resetCoreDataStack
2014-05-29 15:54:35.830 MyApp[10230:303] Reset Core Data
{Exception thrown iff wincon dealloc doesn't unbind everything}

And so the window controller dealloc is definitely called when it's nilled in the autoreleasepool, but MOC reset causes an EXEC_BAD_ACCESS unless that wincon dealloc does unbind on a bunch of crap in the Nib.

So the question is:

Given a Nib owned by a custom window controller (self.wincon) with arrayController objects bound to an external managedObjectContext, what needs to be done to force everything in the Nib to be released and unbound? Is there some step that I'm missing that causes me to have to do this unbinding manually?


[EDIT] Some new debug code:

NSLog(@"Wincon dealloc");

@autoreleasepool {
    // Remove subview to ensure subview dealloc
    [_viewController.view removeFromSuperviewWithoutNeedingDisplay];
    _viewController = nil;

    self.window = nil;
}

@autoreleasepool {

    // Tear down bindings to ensure MOC can reset
    for (NSObject<NSKeyValueBindingCreation>* object in
         @[_watcherAC, _watchersTimesTreeController, _watcherTableView, /*_itemsOutlineView*/])
    {
        NSLog(@"Bindings for %@", [object className]);
        for (NSString* binding in [object exposedBindings]) {
            NSLog(@"BI for %@: %@", binding, [object infoForBinding:binding]);
            [object unbind:binding];
        }
    }

the log below is the bindingInfo for the bindings still alive when dealloc is called for the windowController

2014-05-29 21:00:39.967 SaleWatch[11249:303] Wincon dealloc

2014-05-29 21:00:39.975 SaleWatch[11249:303] Bindings for NSArrayController

2014-05-29 21:00:39.978 SaleWatch[11249:303] Bindings for NSTreeController
2014-05-29 21:00:39.989 SaleWatch[11249:303] BI for contentSet: {
    NSObservedKeyPath = "selection.fetchTimesForOutlineView";
    NSObservedObject = "[entity: SWWebStoreWatcher, number of selected objects: 1]";
}

2014-05-29 21:00:39.991 SaleWatch[11249:303] Bindings for NSTableView
2014-05-29 21:00:39.991 SaleWatch[11249:303] BI for selectionIndexes: {
    NSObservedKeyPath = selectionIndexes;
    NSObservedObject = "[entity: SWWebStoreWatcher, number of selected objects: 1]";
}
2014-05-29 21:00:40.001 SaleWatch[11249:303] BI for content: {
    NSObservedKeyPath = arrangedObjects;
    NSObservedObject = "[entity: SWWebStoreWatcher, number of selected objects: 1]";
}

2014-05-29 21:00:40.020 SaleWatch[11249:303] About to resetCoreDataStack

I forced wincon.window = nil in the new code, and these three objects still aren't nil, though the outlineView the treeController is for did become nil. There could be a retain cycle here, but I don't see how it'd be my fault... yet.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...