iCloud NSDocument保存警告 - 由lastUsedDate文件属性引起?

时间:2018-02-07 21:20:34

标签: icloud nsdocument uidocument nspersistentdocument apfs

我的应用在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有关,因为在[...}}上有一个新方法setLastUsedDate:forItemIdentifier:completionHandler:FinderSync,因为setLastUsedDate:forItemWithURL:completion:也是新。)

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

1 个答案:

答案 0 :(得分:0)

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

首先是关于文件时间戳和文件系统类型的重要说明。 HFS+个时间戳的分辨率为一秒。 APFS timestamps have a resolution of 1 nanosecond

当OS X App的iCloud容器迁移到APFS时,我的问题才开始显现。

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

  1. 在iCloud应用容器中的OS X上打开文件。
  2. 进行更改并将更改保存到文件中,此时APFS上的文件修改日期将包含小数第二个组件。
  3. 在另一台设备上打开相同的文件 - 比如iOS(在允许iCloud传播更改之后)。在iOS上打开文件的修改日期会截断小数秒组件。
  4. 不久之后,回到OS X,最近保存的文件的修改日期将小数秒组件截断(由我无法控制的iCloud进程 - presentedItemDidChange在此处调用时间)。
  5. 如果另一项更改已保存到OS X文件中,则更改警告" 此文档的文件已被其他应用程序更改,因为您已打开或保存该文档。&# 34;会出现。这是因为上次保存的NSDocument self.fileModificationDate(包含小数秒组件)与文件修改日期(已截断小数秒组件)之间不匹配。
  6. 其含义是:

    • 当iCloud在主机之间交换时间戳元数据时,分辨率以秒为单位。
    • 当打开文件,并且时间戳元数据被传送到其他设备时,它是截断的形式。它也会写回最后写入文件的设备上的文件。

    我使用的解决方法(只是因为我使用的NSPersistentDocument不支持自动保存才有必要)是将self.fileModificationDate设置为小数秒截断版本(是的,它被读取) -write),当且仅当 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是非标准的,所以我不知道其他人是否有这个问题。