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

倖福魔咒の 提交于 2020-01-09 05:26:04

问题


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:

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.

来源:https://stackoverflow.com/questions/23944436/should-i-need-to-unbind-cocoa-bindings-in-dealloc-of-windowcontroller

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!