【问题标题】:Document-based app doesn't restore documents with non-file URLs基于文档的应用程序不会恢复具有非文件 URL 的文档
【发布时间】:2012-12-12 21:08:33
【问题描述】:

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

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

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

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

  • --初始化
  • –initWithContentsOfURL:ofType:error:
  • –initForURL:withContentsOfURL:ofType:error:
  • --initWithType:error:

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

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

【问题讨论】:

    标签: cocoa nsdocument nsdocumentcontroller


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

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

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

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

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

      【讨论】:

      • 这对入门很有帮助。事实证明情况更复杂:它实际上是 NSDocument,而不是 NSWindow,它负责在自己的 @987654340 实现中对 document 状态(编号 ID、url、最近更改、类型)进行编码@。 NSDocumentController 使用该状态信息来恢复文档,然后调用-makeWindowControllers 和 NSApplication(或 NSApplication 的完成处理程序?)恢复窗口。此外,当使用 NSDocumentController 的自定义子类时,AppKit 将 that 类设置为 restorationClass
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多