我有使用核心数据绑定的窗口控制器和视图控制器,但我希望能够真正获得这些视图,真正获得解除分配。然后,我想reset
managedObjectContext,并且在那时放弃了尽可能多的内存。
我发现我需要unbind
我在Nib中绑定的内容,或者MOC会保留Nib对象。
问题是这个EXEC_BAD_ACCESS跟踪:
如果我没有取消绑定所有绑定,即使是在Interface Builder中创建的绑定,MOC上的reset
也会导致EXEC_BAD_ACCESS,因为绑定试图反映视图中MOC的变化,通过一个应该消失的阵列控制器,但不是。
所以我在窗口控制器的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];
}
}
}
以这种方式触发:
- (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];
}
...现在我没有收到resetCoreDataStack
上面的堆栈跟踪带有如下日志文件:
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}
所以窗口控制器dealloc在autoreleasepool中被清除时肯定会被调用,但是MOC重置会导致EXEC_BAD_ACCESS,除非wincon dealloc对Nib中的一堆垃圾进行unbind
。
所以问题是:
如果一个自定义窗口控制器(self.wincon
)拥有一个绑定到外部managedObjectContext的arrayController对象的Nib,那么需要做什么来强制Nib中的所有内容被释放和解除绑定?是否有一些我失踪的步骤导致我必须手动取消绑定?
[编辑]一些新的调试代码:
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];
}
}
下面的日志是为windowController调用dealloc时绑定仍然存活的bindingInfo
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
我在新代码中强制wincon.window = nil,这三个对象仍然没有,尽管treeController用于的outlineView确实变为零。这里可能有一个保留周期,但我不知道它是如何成为我的错......但是。