13

我有一个基于 NSDocument 和 NSDocumentController 子类的应用程序。我的 NSDocument 与文件 URL 和具有使用 Web 服务的自定义方案的 URL 一起使用。

我使用自定义代码处理大部分加载和保存,包括-saveToURL:ofType:forSaveOperation:completionHandler:. +autosavesInPlace返回YES

我遇到的问题:具有自定义 URL 方案的文档在启动时未恢复。具有文件 URL 方案的文档是 - 保存到文件的常规文档和自动保存的无标题文档。

离开打开的基于服务器的文档并退出应用程序后,重新启动时似乎没有调用任何 NSDocument 方法。特别是,四个初始化器都没有被调用:

  • -在里面
  • --initWithContentsOfURL:ofType:错误:
  • –initForURL:withContentsOfURL:ofType:error:
  • --initWithType:错误:

NSDocumentController 方法-reopenDocumentForURL:withContentsOfURL:display:completionHandler:也没有被调用。

如何以及何时对文档的可恢复状态进行编码?它们是如何以及何时解码的?

4

2 回答 2

14

NSDocument 负责将其可恢复状态编码到 中-encodeRestorableStateWithCoder:,NSDocumentController 负责解码文档的可恢复状态并重新打开 中的文档+restoreWindowWithIdentifier:state:completionHandler:。请参阅 中的有用评论NSDocumentRestoration.h

当 NSDocument 对 URL 进行编码时,它似乎使用了 NSURL 的书签方法。问题是这些方法仅适用于文件系统 URL。(非文件 URL 可能会编码,但它们不会正确解码。)

要解决此问题,请覆盖使用自定义方案的 NSDocument 实例的编码,以及对这些文档的解码。

NSDocument 子类:

- (void) encodeRestorableStateWithCoder:(NSCoder *) coder {
    if ([self.fileURL.scheme isEqualToString:@"customscheme"])
        [coder encodeObject:self.fileURL forKey:@"MyDocumentAutoreopenURL"];
    else
        [super encodeRestorableStateWithCoder:coder];
}

NSDocumentController 子类:

+ (void) restoreWindowWithIdentifier:(NSString *) identifier
                               state:(NSCoder *) state
                   completionHandler:(void (^)(NSWindow *, NSError *)) completionHandler {

    NSURL *autoreopenURL = [state decodeObjectForKey:@"MyDocumentAutoreopenURL"];
    if (autoreopenURL) {
        [[self sharedDocumentController]
         reopenDocumentForURL:autoreopenURL
         withContentsOfURL:autoreopenURL
         display:NO
         completionHandler:^(NSDocument *document, BOOL documentWasAlreadyOpen, NSError *error) {

             NSWindow *resultWindow = nil;
             if (!documentWasAlreadyOpen) {

                 if (![[document windowControllers] count])
                     [document makeWindowControllers];

                 if (1 == document.windowControllers.count)
                     resultWindow = [[document.windowControllers objectAtIndex:0] window];
                 else {
                     for (NSWindowController *wc in document.windowControllers)
                         if ([wc.window.identifier isEqual:identifier]) {
                             resultWindow = wc.window;
                             break;
                         }
                 }
             }
             completionHandler(resultWindow, error);
         }
         ];
    } else
        [super restoreWindowWithIdentifier:identifier
                                     state:state
                         completionHandler:completionHandler];
}

行为或完成处理程序遵循 Apple 在 NSDocumentRestoration.h 中的方法注释,应该与super's 大致相同。

于 2012-12-20T19:15:04.643 回答
5

窗口状态编码通过两种方法启用NSWindow。调用setRestorable:窗口会将其标记为可以在重新启动时保存和恢复的窗口,然后调用setRestorationClass:允许您指定一个类来处理重新创建已保存的窗口。

默认情况下,AppKit 设置NSDocumentControllerNSDocument对象控制的窗口的恢复类。实际的恢复是通过调用协议+restoreWindowWithIdentifier:state:completionHandler:定义的方法来完成的。NSWindowRestoration对于文档,NSDocumentController实现该方法并根据传递给该方法NSDocument的实例中编码的状态重新创建对象。NSCoder

因此,理论上,如果您要继承NSDocumentController并覆盖该方法,那么您就有机会恢复由状态恢复机制保存的文档。但是,据我所知,用于NSDocumentController存储状态的密钥在任何地方都没有记录,因此我认为没有一种可靠的方法可以直接从NSDocumentController存储自身的状态中恢复。

为了支持这一点,您可能需要自己对文档的整个状态进行编码,方法是实现-encodeRestorableStateWithCoder:正在NSWindow编码的对象,和/或实现window:willEncodeRestorableState:窗口的委托方法。这两种方法都会向您传递一个NSCoder可用于对您的状态进行编码的实例。您可以在此处对自定义方案的 URL 以及保存/恢复状态所需的任何其他相关数据进行编码。然后,您将在restoreWindowWithIdentifier:state:completionHandler:方法中解码该状态。

由于您将拥有一些具有常规文件 URL 的文档和一些具有自定义 URL 的文档,因此我将通过创建一个负责解码文档状态的单独类来解决此问题,并将其设置为仅用于具有自定义 URL 的文档的恢复类,留下NSDocumentController为您处理带有文件 URL 的文档。

于 2012-12-18T16:11:43.207 回答