2

我有一个NSPersistentDocument在 OS X 上使用(没有自动保存)和UIDocument在 iOS 上使用(也没有自动保存)的应用程序。文件表示是二进制核心存储。自 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 中使用。我想知道文件元数据的更新是否触发了这个虚假警告。

(我怀疑这个属性可能与NSFileProvideriOS 11 相关,因为有一种新方法setLastUsedDate:forItemIdentifier:completionHandler:,而FinderSyncmacOS 10.13setLastUsedDate:forItemWithURL:completion:也是新方法。)

我的问题是——其他人看到这种新行为了吗?它是否会引起其他人如此烦人的副作用?

4

1 回答 1

2

我已经进一步研究了这个问题。我已经确定了似乎正在发生的事情,以及解决方法。注意这仅适用于NSPersistentDocument-没有自动保存。

首先是关于文件时间戳和文件系统类型的重要说明。HFS+时间戳的分辨率为一秒。APFS时间戳的分辨率为 1 纳秒

当 OS X 应用程序的 iCloud 容器迁移到APFS.

这是一个典型的序列(我使用 OS X 和 iOS 作为示例设备 - 但无论“其他”iCloud 连接设备的操作系统类型如何,都会发生相同的序列):

  1. 在 iCloud 应用容器中打开 OS X 上的文件。
  2. 进行更改并将更改保存到文件中,此时文件修改日期APFS将包含小数秒部分。
  3. 在另一台设备上打开相同的文件 - 比如 iOS(在允许 iCloud 传播更改之后)。在 iOS 上打开文件的修改日期已截断小数秒部分。
  4. 不久之后,回到 OS X 上,最近保存的文件的修改日期将截断小数秒部分(由我无法控制的 iCloud 进程 -presentedItemDidChange此时调用)。
  5. 如果另一个更改保存到 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是非标准的,不知道别人会不会有这个问题。

于 2018-02-12T08:54:25.137 回答