自动保存非来自用户的 NSDocument 模型更改

Posted

技术标签:

【中文标题】自动保存非来自用户的 NSDocument 模型更改【英文标题】:Autosave NSDocument model changes that do not origin from the user 【发布时间】:2014-01-11 08:02:46 【问题描述】:

我有一个基于NSDocument 的 OS X 应用程序,它不会使用空白页面创建新文档,而是向用户显示一个面板以供从中选择模板,例如 Apple 的 Pages.app。

我通过在用户启动一个新文档时手动创建一个新的 NSDocument 实例并立即在其上设置一个属性来反映用户为其选择的模板来做到这一点:

MyNSDocumentSubclass *newDoc = [sharedDocumentController makeUntitledDocumentOfType:fileType error:&err];
[newDoc setTemplate:templateChosenByUser]; // autosave doesn't care about this line
[sharedDocumentController addDocument:newDoc];
[newDoc makeWindowControllers];
[newDoc showWindows];

在用户决定重新启动计算机或关闭应用程序而不保存之前,这可以正常工作:当 App Kit 的自动保存功能尝试在下次启动时恢复我的应用程序的以前状态时,它无法为用户模板属性执行此操作因为它从来没有注意到MyNSDocumentSubclass'模型状态已经改变并且没有自动保存整个文档。相反,可能出于性能优化的原因,它只是创建了一个新的MyNSDocumentSubclass 实例,并且用户的模板选择丢失了。

为了解决这个问题,我添加了

[newDoc updateChangeCount:NSChangeDone];

反映用户选择模板引入的模型机会。现在,自动保存正确启动,并在计算机或应用程序关闭之前保存文档。唯一的缺点(以及我在这里寻求帮助的问题)是,这是错误的做法:NSChangeDone 旨在反映用户发起的文档模型的机会。因此,它会导致“已编辑”已显示在新文档的窗口中,并在关闭窗口时向用户显示保存面板。但是,用户的模板选择并不是真正应该算作“编辑”的更改,因为它与新文档的创建紧密相关。幸运的是,OS X 发行说明正是讨论了这个问题并提供了解决方案:

某些应用程序使用-updateChangeCount: 导致NSDocument 自动保存并非直接源自用户的更改。例如,当导入非本地文档类型时,某些应用程序会使用导入的内容创建一个新文档并调用 -updateChangeCount: 以确保文档自动保存这些内容。许多应用程序为此目的使用NSChangeDone。但是,由于用户没有明确地导致此更改,因此不希望将此文档变成草稿。应用程序应小心使用正确的NSDocumentChangeType——在本例中为NSChangeReadOtherContents——以防止转换为草稿。使用NSChangeDiscardable 也会阻止草稿的创建。

不幸的是,[newDoc updateChangeCount:NSChangeReadOtherContents] 根本不起作用。它肯定会抑制窗口标题中的“已编辑”,但同时它会阻止自动保存在终止应用程序时完成其工作:文档不会自动保存并在下次启动时丢失其模板属性的值。

那么,我该怎么办? 我希望我新创建的NSDocument 子类能够自动保存用户的模板选择。同时,它不应该显示为已经被用户编辑,而实际上他们只是创建了它。

我唯一能想到的就是尝试使用NSWindowRestoration 协议来保存模板属性,但这显然是错误的。此外,在文档重新打开过程中,窗口恢复启动得太晚了。 我的另一个想法是为用户可以选择的每个模板创建几个不同的 NSDocument 子类(由具有单个 UTI 的不同文件类型反映),而不是在我的 NSDocument 子类上使用属性——但这感觉还是不对的。 否则,我迷路了。感谢您的帮助。

【问题讨论】:

【参考方案1】:

我认为不会有一种干净的方式来做你想做的事,因为从根本上说,你试图将用户设置的内容存储在自动保存文件中,并且没有让 NSDocument 注意到文件是“脏的”,当按照 Apple 的定义,它实际上是肮脏的。

但是,我认为你可以完成你想要的,如果你愿意变得有点肮脏。一个想法是简单地做类似的事情:

[newDoc updateChangeCount:NSChangeDone];
[newDoc autosaveWithImplicitCancellability:NO completionHandler:^(NSError *errorOrNil)
    [newDoc updateChangeCount:NSChangeCleared];
];

我知道,我知道,这不干净。

【讨论】:

它可能不干净,但它是一个实用的解决方案,似乎工作正常。谢谢! 当我在基于文档的应用程序中使用代码更改文档时,编写上述代码可防止“无法自动保存文档 APP_NAME”系统错误消息。谢谢你分享这个。【参考方案2】:

您是否尝试过使用 -[NSDocument readFromURL:ofType:error:] 或 -[NSDocument readFromData:ofType:error:] 方法之一将模板的内容加载到空的 NSDocument 实例中?

【讨论】:

以上是关于自动保存非来自用户的 NSDocument 模型更改的主要内容,如果未能解决你的问题,请参考以下文章

从“NSDocument 即将关闭工作表”截取保存

如何同时保存 NSDocument?

NSDocument 唯一标识符

从 NSObject 获取 NSDocument

如何在 Django 中从用户模型的字段自动填充和显示数据到来自不同应用程序的另一个模型?

NSDocument 子类是不是是模型对象?