基于文档的应用程序不会还原包含非文件URL的文档

时间:2012-12-12 21:08:33

标签: cocoa nsdocument nsdocumentcontroller

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

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

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

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

  • -init
  • -initWithContentsOfURL:ofType:错误:
  • -initForURL:withContentsOfURL:ofType:错误:
  • -initWithType:错误:

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

文件的可恢复状态如何以及何时编码?他们如何以及何时解码?

2 个答案:

答案 0 :(得分:13)

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大致相同。

答案 1 :(得分:4)

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

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

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

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

由于您将拥有一些包含常规文件URL的文档,而某些文档包含您的自定义URL,因此我会通过创建一个负责解码文档状态的单独类来实现此目的,并将其设置为仅用于自定义文档的修复类URL,让NSDocumentController处理带有文件网址的文档。