如何为观察到的属性实现状态恢复

时间:2017-04-19 18:42:50

标签: objective-c macos cocoa key-value-observing state-restoration

我有一个基于文档的Cocoa应用程序,我想要实现状态恢复。具体来说,我的NSDocument子类包含一个需要保留其某些属性的控制器对象。问题是其中一个窗口控制器在控制器恢复之前将自身注册为控制器的观察者。因此,当控制器恢复时,应用程序会抛出异常,因为它在已经被观察时尝试释放默认控制器。

类DataController的实例0x600000001ba0已取消分配,而键值观察者仍在其中注册。

我创建了一个重现问题的最小例子。结构如下:

RestorationExample
|-> Document.h
|-> Document.m // Custom subclass of NSDocument, has DataController property
|-> DataController.h
|-> DataController.m // Controller that needs to be restored
|-> MainWindowController.h
|-> MainWindowController.m // Observes self.document.dataController
|-> MainWindow.xib // Window controlled by MainWindowController

您可以从此处下载示例项目,以便更轻松地设置所有内容:https://github.com/mpflanzer/restoration_example

Document 只会创建MainWindowController

- (void)makeWindowControllers {
    [self addWindowController:[[MainWindowController alloc] initWithWindowNibName:@"MainWindow"]];
}

并编码/恢复其dataController属性

- (void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
    NSLog(@"Document::encode");
    [super encodeRestorableStateWithCoder:coder];
    [coder encodeObject:self.dataController forKey:@"dataController"];
}

- (void)restoreStateWithCoder:(NSCoder *)coder
{
    NSLog(@"Document::restore");
    [super restoreStateWithCoder:coder];
    self.dataController = [coder decodeObjectForKey:@"dataController"];
}

MainWindowController windowDidLoad方法中将自己注册为观察者,并在windowWillClose中取消注册。 这是怎么做的?或者是否有其他功能应该用于添加或删除观察者?

- (void)windowDidLoad {
    NSLog(@"MainWindowController::windowDidLoad");
    [super windowDidLoad];
    [((Document*)self.document).dataController addObserver:self forKeyPath:@"dataOffset" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil];
}

- (void)windowWillClose:(NSNotification *)notification
{
    [((Document*)self.document).dataController removeObserver:self forKeyPath:@"dataOffset"];
}

目前 DataController 并没有做任何特别的事情,因为该应用已经失败。后来显然必须实现initWithCoder来恢复其属性。

当我运行示例(并触发状态恢复)时,我得到以下日志输出:

2017-04-19 18:57:54.846297+0100 RestorationExample[16829:1225236] Document::init
2017-04-19 18:57:54.846440+0100 RestorationExample[16829:1225236] DataController::init
2017-04-19 18:57:54.887825+0100 RestorationExample[16829:1225236] Document::readFromData
2017-04-19 18:57:54.953205+0100 RestorationExample[16829:1225236] MainWindowController::windowDidLoad
2017-04-19 18:57:54.953386+0100 RestorationExample[16829:1225236] Change data offset: 0
2017-04-19 18:57:54.957346+0100 RestorationExample[16829:1225236] Document::restore
2017-04-19 18:57:54.958678+0100 RestorationExample[16829:1225236] Ignoring exception raised in void _NSPersistentUIExecuteDispatchedBlock(void (^)(void)): An instance 0x600000001c20 of class DataController was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x60000002ae00> (
<NSKeyValueObservance 0x600000056050: Observer: 0x600000088a70, Key path: dataOffset, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x60000005e9c0>
)
2017-04-19 18:57:59.592119+0100 RestorationExample[16829:1225236] Document::encode

问题是在makeWindowControllers恢复之前调用了windowDidLoad,因此Document。鉴于该命令,MainWindowController将注册为观察者,并且仅在Document之后将尝试将其dataController属性恢复为已保存状态。

我是如何实施状态恢复的?或者在我添加/删除观察者的方式?我原本期望首先恢复Document,然后再调用makeWindowControllers

0 个答案:

没有答案