我已经进一步研究了这个问题。我已经确定了似乎正在发生的事情,以及解决方法。注意这仅适用于NSPersistentDocument
-没有自动保存。
首先是关于文件时间戳和文件系统类型的重要说明。HFS+
时间戳的分辨率为一秒。APFS
时间戳的分辨率为 1 纳秒。
当 OS X 应用程序的 iCloud 容器迁移到APFS
.
这是一个典型的序列(我使用 OS X 和 iOS 作为示例设备 - 但无论“其他”iCloud 连接设备的操作系统类型如何,都会发生相同的序列):
- 在 iCloud 应用容器中打开 OS X 上的文件。
- 进行更改并将更改保存到文件中,此时文件修改日期
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
是非标准的,不知道别人会不会有这个问题。