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):
- Откройте файл на OS X в контейнере приложения iCloud.
- Внесите изменения и сохраните изменения в файле, на этом этапе дата изменения файла
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
не является стандартным, я не знаю, могут ли другие иметь эту проблему.