2013-03-05 20 views
5

Mam aplikację opartą na UIDocument, która używa NSFileWrapper s do przechowywania danych. Opakowanie "master" zawiera wiele dodatkowych obwolut plików katalogowych, z których każdy reprezentuje inną stronę dokumentu.UIDocument i NSFileWrapper - NSFastEnumerationMutationHandler podczas zmiany pakowania pliku podczas zapisywania

Ilekroć dokonuję zmiany w dokumencie, gdy zapisuje się UIDocument (w writeContents:andAttributes:safelyToURL:forSaveOperation:error:), aplikacja ulega awarii. Oto ślad stosu:

UIDocument crash stack trace

Wydaje się oczywiste, że jestem taką samą modyfikację instancję pliku wrapper że UIDocument wylicza się w tle. Rzeczywiście, sprawdziłem, że zwracając migawkę modelu danych w contentsForType:error:, zwinięte obwoluty pliku podrzędnego wskazują te same obiekty, które obecnie są rezydowane (i są edytowane) w modelu danych, a nie kopie.

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

to karane podejściem do realizacji tego sposobu (według WWDC 2012 Session 218 - Using iCloud with UIDocument).

Więc przypuszczam, że pytanie brzmi: Jak to podejście może być bezpieczne dla wątków?

Czy sytuacja wygląda inaczej, gdy opakowanie zbiorcze pliku fileWrappers jest samo w sobie owijką pliku katalogowego? Jeśli usankcjonowane podejście jest błędne, należy je wykonać?

+0

Nie natknąłem się na tę sytuację, ale wygląda na to, że NSFileCoordinator może wykonać to zadanie? –

+0

@MikeM Możesz mieć rację, ponieważ zapobiegnie to awarii, ale obawiam się, że może naprawdę spowolnić działanie. Często aktualizacje w aplikacji są małe i częste, a aktualna treść jest wymagana, aby aplikacja mogła szybko reagować. Będę musiał zbadać to podejście dalej i sprawdzić, czy jest on wykonalny. Pozostaje jednak pytanie - czy sankcjonowane podejście do UIDocumentu nie jest bezpieczne? – Stuart

Odpowiedz

6

Jeśli dzwonisz jedną z metod writeContents:..., nie powinieneś. Powinieneś zamiast tego dzwonić pod numer saveToURL:forSaveOperation:completionHandler:. Metody writeContents:... są przeznaczone do zaawansowanej podklasy.

UIDocument wykorzystuje dwa wątki - główny wątek, a „UIDocument dostęp do plików” gwint (który, jeśli podklasy więcej UIDocument można zrobić rzeczy poprzez).

Bezpieczeństwo nici z UIDocument jest jak wszystko w Celu C - pozwala tylko zmodyfikować wątek będący właścicielem obiektu. Jeśli obiekt, który chcesz zmienić, jest odczytywany, ustaw go w kolejce do zmiany po zakończeniu zapisu. Być może zmień inny obiekt należący do podklasy UIDocument i przeciągnij go do nowego NSFileWrapper w contentsForType:error:. Przekaż kopię pliku FileWrappers NSDictionary.

NSFileWrapper faktycznie ładuje cały dokument do pamięci. Numer NSFileWrapper jest rzeczywiście tworzony w wątku "UIDocument File Access" w metodzie readFromURL:error:, który następnie jest przekazywany do metody loadFromContents:ofType:error:. Jeśli masz duży dokument, może to chwilę potrwać.

Podczas zapisywania zazwyczaj chcem UIDocument zdecydować, kiedy to zrobić, i niech wiedzą, że coś się zmieniło metodą updateChangeCount: (param jest UIDocumentChangeDone). Jeśli chcesz teraz zapisać coś, co chcesz, możesz teraz użyć, aby użyć metody saveToURL:forSaveOperation:completionHandler:.

Jeszcze jedna rzecz, na którą należy zwrócić uwagę to UIDocument implementuje protokół NSFilePresenter, który definiuje metody, które należy zastosować w przypadku NSFileCoordinator. UIDocument koordynuje tylko pisanie na głównym dokumencie, a nie na podfile.Można by pomyśleć, że koordynowanie podtekstów w dokumencie może pomóc, ale awaria, którą otrzymujesz, jest związana z mutowaniem słownika podczas iteracji, więc nie pomożemy. Musisz tylko martwić się o pisanie własnego NSFilePresenter, jeśli (1) chcesz otrzymywać powiadomienia o zmianach w pliku, lub (2) inny obiekt lub aplikacja odczytuje/zapisuje do tego samego pliku. To, co UIDocument już działa, będzie działało dobrze. Jednak podczas przenoszenia/usuwania całych dokumentów użytkownik chce korzystać z NSFileCoordinator.

+0

Dzięki za odpowiedź. Rozumiem już większość z tego, o czym wspomniałeś (ale dobrze jest tu krótko powiedzieć tutaj), np. Nadpisuję 'writeContents: ...' i wywołuję jego super implementację w celu implementacji zapisywania podglądu i używam 'updateChangeCount:' do oznaczenia wymaganych zapisów. Jestem również świadomy, że 'UIDocument' obsługuje koordynację plików, jednak nie skoordynowane zapisywanie w opakowaniu pliku root implikuje koordynację w podfiles? Z Przewodnika po programowaniu systemu plików: "Uwaga: Kiedy instancja' NSFileWrapper' jest określona jako pozycja do koordynacji, wszystkie pliki ... – Stuart

+0

... w obrębie pliku są automatycznie częścią koordynacji tego pliku. ". Obecnie używam osobnego obiektu danych do przechowywania danych (w instancjach "strony" posiadanych przez moją podklasę "UIDocument"), ale zaraz po wprowadzeniu zmiany umieszczam nowe opakowanie pliku pod otoką pliku głównego jako odbywa się w przykładowej aplikacji Apple CloudNotes, zamiast czekać i dodawać ją w 'contentsForType:'. Jak podkreślasz, to na pewno jest problem. Będę odraczał aktualizacje do pliku głównego, dopóki "UIDocument" nie poprosi o migawkę i sprawdzenie, czy wszystko działa dobrze. Dzięki jeszcze raz. – Stuart

+0

Dokumentacja jest zagmatwana i miałem problemy tak jak Ty. Pomyślałem, że pokryję tyle, ile zdołam. Prawdopodobnie chcesz zrobić podgląd, zapisując gdzieś indziej. CloudNotes robi pewne zwariowane rzeczy - zapisuje drugi UIDocument dla podglądu. Naprawdę musisz to zrobić, jeśli nie zawsze zachowujesz lokalną kopię każdego dokumentu. Tak, jeśli skoordynujesz główny dokument, możesz go zastosować do plików podrzędnych, ale, o ile mi wiadomo, zakłada on, że inna aplikacja/obiekt koordynuje główny dokument (iCloud i UIDocument to robią). – Luke

Powiązane problemy