2013-03-05 2 views
5

У меня есть приложение на основе UIDocument, которое использует NSFileWrapper s для хранения данных. Файловая оболочка «мастер» содержит много дополнительных обложек файлов каталога, каждая из которых представляет собой другую страницу документа.UIDocument & NSFileWrapper - NSFastEnumerationMutationHandler при изменении обертки файла во время сохранения

Когда я вношу изменения в документ, а UIDocument сохраняет (в writeContents:andAttributes:safelyToURL:forSaveOperation:error:), приложение выходит из строя. Вот трассировки стека:

UIDocument crash stack trace

Кажется, ясно, что я модифицируя тот же экземпляр файла обертке, что UIDocument будет перечисляющем над на заднем плане. Действительно, я проверил, что при возврате моментального снимка модели данных в contentsForType:error: возвращенные обертки субфайлов указывают на те же объекты, что и те, которые в настоящее время находятся (и редактируются) в модели данных, а не копии.

- (id)contentsForType:(NSString *)typeName error:(NSError *__autoreleasing *)outError 
{ 
    if (!_fileWrapper) { 
     [self setupEmptyDocument]; 
    } 
    return [[NSFileWrapper alloc] initDirectoryWithFileWrappers:[_fileWrapper fileWrappers]]; 
} 

Это санкционировано подход к реализации этого метода (в соответствии с WWDC 2012 Session 218 - Using iCloud with UIDocument).

Итак, вопрос в следующем: Как этот подход может быть потокобезопасным?

Является ли ситуация как-то иначе, когда обертки файла главного файла fileWrappers сами являются обертками файлов каталога? Если санкционированный подход ошибочен, то как должен это сделать?

+0

Я не сталкивался с этой ситуацией, но похоже, что NSFileCoordinator может выполнить эту работу? –

+0

@MikeM Возможно, вы правы в том, что это предотвратит крах, но я беспокоюсь, что у него есть потенциал, чтобы действительно замедлить работу. Часто обновления в приложении небольшие и частые, и для приложения требуется обновленный контент. Мне придется еще раз изучить этот подход и посмотреть, насколько он жизнеспособен. Однако до сих пор остается вопрос - является ли санкционированный подход к использованию UIDocument безопасным для потоков? – Stuart

ответ

6

Если вы вызываете какие-либо из методов writeContents:..., вы не должны быть. Вместо этого вы должны называть saveToURL:forSaveOperation:completionHandler:. Методы writeContents:... предназначены для расширенного подкласса.

UIDocument использует две нити - основная нить и «UIDocument доступа к файлам» нить (который, если вы подклассы больше UIDocument, вы можете сделать что-то в через).

Безопасность резьбы с UIDocument - это как-нибудь в Objective C - разрешить нить, владеющую объектом, изменить ее. Если объект, который вы хотите изменить, читается, очередь его должна быть изменена после завершения записи. Возможно, измените другой объект, принадлежащий вашему подклассу UIDocument, и потяните их в новый NSFileWrapper в contentsForType:error:. Пропустите копию файлаWrappers NSDictionary.

NSFileWrapper фактически загружает весь документ в память. NSFileWrapper фактически создан в потоке доступа к файлу UIDocument в методе readFromURL:error:, который затем передается методу loadFromContents:ofType:error:. Если у вас большой документ, это может занять некоторое время.

При сохранении обычно вы хотите, чтобы UIDocument решила, когда это сделать, и сообщите ему, что что-то изменилось с помощью метода updateChangeCount: (параметр UIDocumentChangeDone). Если вы хотите что-то сохранить прямо сейчас вы хотите использовать метод saveToURL:forSaveOperation:completionHandler:.

Следует отметить, что UIDocument использует протокол NSFilePresenter, который определяет методы использования NSFileCoordinator. UIDocument только координирует запись в корневом документе, а не в подфайлах.Вы можете подумать, что координирование подфайлов внутри документа может помочь, но сбой, который вы получаете, связан с изменением словаря во время его повторения, поэтому это не поможет. Вам нужно только беспокоиться о написании собственного NSFilePresenter, если вы (1) хотели получать уведомления об изменениях файла или (2) другой объект или приложение читал/записывал в тот же файл. Что UIDocument уже будет работать нормально. Однако вы хотите использовать NSFileCoordinator при перемещении/удалении всех документов.

+0

Спасибо за ваш ответ. Я уже понимаю большую часть того, что вы упомянули (но хорошо, если бы это было сказано кратко), например. Я переопределяю 'writeContents: ...' и называю его супер-реализацию, чтобы реализовать сохранение предварительного просмотра, и я использую 'updateChangeCount:' для отметки необходимых сохранений. Я также знаю, что 'UIDocument' обрабатывает координацию файлов, но не скоординирует ли запись в корневой файловой оболочке, подразумевая координацию на подфайлах? Из руководства по программированию файловой системы: «Примечание: когда экземпляр« NSFileWrapper »указан как элемент для согласования, все файлы ... – Stuart

+0

... в файловой оболочке автоматически являются частью этой координации файлов». В настоящее время я использую отдельный объект данных для хранения данных (в экземплярах 'Page', принадлежащих моему подклассу' UIDocument'), но как только происходит изменение, я помещаю новую оболочку файла под корневую файловую оболочку как выполняется в примере приложения Apple CloudNotes, а не в ожидании и добавлении его в 'contentForType:'. Как вы заметили, это, безусловно, проблема. Я буду откладывать обновления корневой файловой оболочки до тех пор, пока 'UIDocument' не запросит моментальный снимок и не увидит, все ли работает хорошо. Еще раз спасибо. – Stuart

+0

Документация в замешательстве, и у меня были проблемы, как и вы. Поэтому я решил, что я покрою столько, сколько смогу. Вероятно, вы хотите сохранить предварительный просмотр в другом месте. CloudNotes делает некоторые дурацкие вещи - это сохраняет второй UIDocument для предварительного просмотра. Вам действительно нужно сделать это, если вы не всегда держите локальную копию каждого документа. Да, если вы координируете корневой документ, он может применяться к подфайлам, но, насколько мне известно, предполагается, что другое приложение/объект координирует корневой документ (iCloud & UIDocument делает это). – Luke

Смежные вопросы