我有一个应用程序在OS X上使用NSPersistentDocument
(不自动保存),在iOS上使用UIDocument
(也不自动保存)。文件表示形式为二进制核心存储。自iOS 7 + macOS 10.10以来,此应用程序一直运行良好。
如果我在OS X 10.13上打开文稿,而另一台设备(macOS 10.13或iOS 11)打开了同一个文件,则在下次保存时,我会收到一条警告"自您打开或保存文稿以来,此文稿的文件已被其他应用程序更改。警告是虚假的,因为另一台设备上只发生了打开 - 而不是保存。
在寻找此通知的可能原因时,我注意到当一台设备上打开iCloud文件时,会更新名为com.apple.lastuseddate#PS
的扩展属性。我已经确认此扩展属性已在iOS 11和macOS 10.13上更新。此扩展属性似乎未在以前版本的 iOS 或 macOS 中使用。我想知道文件元数据的更新是否触发了这个虚假警告。
(我怀疑此属性可能与iOS 11上的NSFileProvider
有关,因为macOS 10.13上setLastUsedDate:forItemIdentifier:completionHandler:
和FinderSync
新方法setLastUsedDate:forItemWithURL:completion:
因为也是新的。
我的问题是 - 其他人看到这种新行为了吗?它是否给别人带来了如此烦人的副作用?
我进一步研究了这个问题。我已经确定了似乎发生了什么,以及解决方法。请注意,这仅适用于NSPersistentDocument
-不自动保存。
首先是关于文件时间戳和文件系统类型的重要说明。HFS+
时间戳的分辨率为一秒。APFS
时间戳的分辨率为 1 纳秒。
我的问题只有在OS X App的iCloud容器迁移到APFS
时才开始显现出来。
这是一个典型的序列(我使用OS X和iOS作为示例设备 - 但无论"其他"iCloud连接设备的操作系统类型如何,都会发生相同的顺序):
- 在 iOS X 上的 iCloud app 容器中打开文件。
- 进行更改并将更改保存到文件中,此时
APFS
上的文件修改日期将具有小数秒分量。 - 在另一台设备上打开相同的文件 - 比如iOS(在允许iCloud传播更改后)。在 iOS 上打开文件的修改日期截断了秒分部分。
- 不久之后,回到OS X上,最近保存的文件的修改日期将截断秒分部分(由我无法控制的iCloud进程截断 - 此时调用
presentedItemDidChange
)。 - 如果将其他更改保存到 OS X 文件,则会出现更改警告"此文稿的文件在您打开或存储后已被其他应用程序更改。这是因为上次保存(包含秒小数部分)的
NSDocument
self.fileModificationDate
与文件修改日期(秒
分数部分已被截断)不匹配。
其含义是:
- 当 iCloud 在主机之间交换时间戳元数据时,分辨率以秒为单位。
- 打开文件并将时间戳元数据传送到其他设备时,它将采用截断形式。它还会写回上次写入文件的设备上的文件。
我采用的解决方法(仅因为我使用没有自动保存的NSPersistentDocument
是必需的)是将self.fileModificationDate
设置为秒截断的小数部分版本(是的,它是读写的),当且仅当self.fileModificationDate
与忽略秒小数部分的文件修改日期匹配时。如果这样做,则不会显示警告 - 并且不会造成任何伤害(因为文件尚未修改):
// saving file changes
NSDate *modDate = nil;
[self.fileURL getResourceValue:&modDate forKey:NSURLContentModificationDateKey error:NULL];
if (modDate && self.fileModificationDate) {
NSTimeInterval delta = [modDate timeIntervalSinceDate:self.fileModificationDate];
if (fabs(delta) > 0.0 && [modDate ss_isEqualToDateInSeconds:self.fileModificationDate])
self.fileModificationDate = modDate.ss_dateWithDateInSeconds;
}
[self saveDocumentWithDelegate:self
didSaveSelector:@selector(documentSaved:didSave:contextInfo:)
contextInfo:nil];
正如我一开始所说的那样——令人惊讶的行为。如果它被记录下来就好了。因为我对NSDocument
的使用是非标准的,我不知道其他人是否有这个问题。