iCloud NSDocument сохранить предупреждения - вызвано атрибутом файла lastUsedDate?

У меня есть приложение, которое использует NSPersistentDocument (без автосохранения) на OS X и UIDocument (также без автосохранения) на iOS. Представлением файла является Binary Core Storage. Это приложение работает нормально с 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. Интересно, вызывает ли обновление метаданных файла это ложное предупреждение.

(Я подозреваю, что этот атрибут может быть связан с NSFileProvider на iOS 11 как появился новый метод setLastUsedDate:forItemIdentifier:completionHandler: а также FinderSync на macOS 10.13 as setLastUsedDate:forItemWithURL:completion: тоже новый.)

Мой вопрос - видят ли другие это новое поведение? Это вызывает другие раздражающие побочные эффекты?

1 ответ

Я изучил эту проблему дальше. Я определил, что, кажется, происходит, и также обходной путь. ПРИМЕЧАНИЕ это относится только к NSPersistentDocument - без автосохранения.

Во-первых, важно отметить временные метки файлов и тип файловой системы. HFS+ временные метки имеют разрешение на одну секунду. APFS временные метки имеют разрешение 1 наносекунда.

Мои проблемы начали проявляться только после переноса контейнера iCloud приложения OS X в APFS,

Вот типичная последовательность (я использовал OS X и iOS в качестве примера устройств, но такая же последовательность происходит независимо от типа ОС для "другого" устройства, подключенного к iCloud):

  1. Откройте файл на OS X в контейнере приложения iCloud.
  2. Внесите изменения и сохраните изменения в файле, на этом этапе дата изменения файла APFS будет иметь дробный второй компонент.
  3. Откройте тот же файл на другом устройстве - скажем, iOS (после того, как iCloud предоставит момент для распространения изменений). Дата модификации при открытии файла на iOS имеет сокращенный компонент долей секунды.
  4. Вскоре после этого в OS X дата модификации недавно сохраненного файла будет усечена за доли секунды (из-за процессов iCloud, не зависящих от меня - presentedItemDidChange называется в это время).
  5. Если в файл OS X сохранено другое изменение, появится предупреждение об изменении " Файл этого документа был изменен другим приложением с момента его открытия или сохранения ". Это из-за несоответствия между NSDocumentself.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 не является стандартным, я не знаю, могут ли другие иметь эту проблему.

Другие вопросы по тегам