2013-03-05 9 views
5

Ho un'app basata su UIDocument che utilizza NSFileWrapper s per archiviare i dati. Il wrapper di file "master" contiene molti wrapper di file di directory aggiuntivi, ognuno dei quali rappresenta una pagina diversa del documento.UIDocument & NSFileWrapper - NSFastEnumerationMutationHandler durante la modifica del file wrapper durante un salvataggio

Ogni volta che apporto una modifica al documento mentre è in corso il salvataggio dello UIDocument (in writeContents:andAttributes:safelyToURL:forSaveOperation:error:), l'app si arresta in modo anomalo. Ecco l'analisi dello stack:

UIDocument crash stack trace

Sembra chiaro che sto modificando la stessa istanza di involucro di file che il UIDocument enumera sopra in background. Effettivamente, ho verificato che quando si restituisce un'istantanea del modello di dati in contentsForType:error:, i wrapper del file secondario restituiti puntano agli stessi oggetti di quelli attualmente residenti (e in fase di modifica) nel modello di dati e non alle copie.

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

Questo è l'approccio sanzionato per l'attuazione di questo metodo (secondo WWDC 2012 Session 218 - Using iCloud with UIDocument).

Quindi suppongo che la domanda sia: In che modo questo approccio può essere sicuro?

La situazione è in qualche modo diversa quando i wrapper del file master fileWrappers sono essi stessi wrapper di file di directory? Se l'approccio sanzionato è sbagliato, come dovrebbe essere ?

+0

Non ho incontrato questa situazione, ma sembra che un NSFileCoordinator potrebbe fare il lavoro? –

+0

@MikeM Forse hai ragione nel prevenire l'incidente, ma temo che abbia il potenziale per rallentare davvero le cose. Spesso gli aggiornamenti dell'app sono piccoli e frequenti e per l'applicazione è necessario un contenuto aggiornato. Dovrò approfondire questo approccio e vedere se è fattibile. Tuttavia, la domanda rimane ancora: l'approccio sanzionato all'utilizzo di UIDocument non è sicuro? – Stuart

risposta

6

Se si sta chiamando uno dei metodi writeContents:..., non si dovrebbe. Dovresti chiamare lo saveToURL:forSaveOperation:completionHandler:. I metodi writeContents:... sono pensati per la sottoclasse avanzata.

UIDocument utilizza due thread - il thread principale e il filo "UIDocument File Access" (che, se si sottoclasse più UIDocument, si possono fare le cose in via).

La sicurezza del thread con UIDocument è simile a qualsiasi cosa in Objective C - consente solo al thread proprietario di un oggetto di modificarlo. Se l'oggetto che si desidera modificare è in fase di lettura, accodalo per essere modificato al termine della scrittura. È possibile modificare un altro oggetto di proprietà della sottoclasse UIDocument e inserirli in un nuovo NSFileWrapper in contentsForType:error:. Passa una copia del file wrapper NSDictionary.

NSFileWrapper carica effettivamente l'intero documento in memoria. Lo NSFileWrapper viene effettivamente creato nella discussione "Accesso file UIDocument" nel metodo readFromURL:error:, che viene quindi passato al metodo loadFromContents:ofType:error:. Se hai un documento di grandi dimensioni questo può richiedere un po 'di tempo.

Quando si effettua il salvataggio, in genere si desidera consentire a UIDocument di decidere quando eseguire questa operazione e far sapere che qualcosa è cambiato tramite il metodo updateChangeCount: (il parametro è UIDocumentChangeDone). Se vuoi risparmiare qualcosa al momento vuoi utilizzare il metodo saveToURL:forSaveOperation:completionHandler:.

Un'altra cosa da notare è UIDocument implementa il protocollo NSFilePresenter, che definisce i metodi per NSFileCoordinator da utilizzare. Il UIDocument coordina solo la scrittura sul documento radice, non i file secondari.Potresti pensare che coordinare i file secondari all'interno del documento possa essere d'aiuto, ma il crash che stai ottenendo è legato alla mutazione di un dizionario mentre viene iterato, quindi non ti aiuterà. Devi solo preoccuparti di scrivere il tuo NSFilePresenter se (1) vuoi ricevere le notifiche delle modifiche ai file o (2) un altro oggetto o app sta leggendo/scrivendo sullo stesso file. Che cosa fa già UIDocument funzionerà correttamente. Tuttavia, si consiglia di utilizzare NSFileCoordinator durante lo spostamento/eliminazione di interi documenti.

+0

Grazie per la risposta. Capisco già la maggior parte di ciò che hai menzionato (ma è bello averlo dichiarato succintamente qui), ad es. Sto sovrascrivendo 'writeContents: ...' e chiamando la sua super implementazione per implementare il salvataggio dell'anteprima, e sto usando 'updateChangeCount:' per segnalare i salvataggi richiesti. Sono anche consapevole del fatto che 'UIDocument' gestisce il coordinamento dei file, tuttavia la scrittura non coordinata sul wrapper dei file radice implica il coordinamento sui file secondari? Dalla Guida alla programmazione del file system: "Nota: quando viene specificata un'istanza' NSFileWrapper' come elemento per il coordinamento, tutti i file ... – Stuart

+0

... all'interno del wrapper dei file fanno automaticamente parte del coordinamento di quel file. ". Attualmente sto usando un oggetto dati separato per archiviare i dati (in istanze di 'Page' possedute dalla mia sottoclasse' UIDocument'), ma appena viene apportata una modifica sto mettendo un nuovo wrapper di file sotto il wrapper di file radice come è fatto nell'app sample CloudNotes di Apple, piuttosto che aspettare e aggiungerlo in 'contentsForType:'. Come fai notare, questo è sicuramente il problema. Rimanderò gli aggiornamenti al wrapper del file radice fino a quando 'UIDocument' richiede un'istantanea e vediamo se tutto funziona correttamente. Grazie ancora. – Stuart

+0

La documentazione è confusa e ho avuto problemi come hai fatto tu. Quindi ho pensato di coprire il più possibile. Probabilmente vorrai fare un'anteprima di salvataggio da qualche altra parte. CloudNotes fa alcune cose stravaganti - salva un secondo UIDocument per un'anteprima. Hai davvero bisogno di farlo solo se non tieni sempre una copia locale di ciascun documento. Sì, se si coordina il documento di root esso può essere applicato ai subfile, ma, per quanto ne so, questo presuppone che l'altra app/oggetto coordini il documento di root (iCloud e UIDocument lo fanno). – Luke

Problemi correlati