2013-03-05 13 views
6

Ich habe eine UIDocument basierte Anwendung, die NSFileWrapper s verwendet, um Daten zu speichern. Der "Master" -Datei-Wrapper enthält viele zusätzliche Verzeichnisdatei-Wrapper, von denen jeder eine andere Seite des Dokuments darstellt.UIDocument & NSFileWrapper - große Dateien eine lange Zeit zu sparen, trotz inkrementellen Änderungen

Wenn ein großes Dokument, für das Speichern nur ein kleiner Teil einer Seite geändert wurde, verbringt UIDocument eine lange Zeit im Hintergrund die Änderungen (in writeContents:andAttributes:safelyToURL:forSaveOperation:error:) zu schreiben. Sicherlich sollte es nur eine kleine Änderung am Datei-Wrapper schreiben ... was dauert so lange?

Meine contentsForType:error: Überschreibung gibt einen neue Verzeichnisdatei Wrapper mit dem Inhalt des Master-Datei-Wrapper (à la WWDC 2012 Session 218 - Using iCloud with UIDocument):

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

Und hier ist ein schönes Bild von einem Stack-Trace von Time Profiler:

UIDocument slow write stack trace

übrigens sagt es ~ 1,6s in diesem Arbeitsthread zu speichern - in der tatsächlichen Laufzeit dies etwa 8 Sekunden gleichgesetzt.


Edit:

Gibt es irgendeine Art und Weise kann ich überprüfen, ob die Datei-Wrapper auf die Festplatte zu schreiben erfordert oder nicht? Nur so kann ich bestätigen, dass ich nicht irgendwie etwas Seltsames mache, wie jeden Unterdatei-Wrapper zu aktualisieren, wenn ich eine kleine Änderung mache (obwohl ich mir sicher bin, dass ich nicht ... bin).


Edit:

Ich hatte einen weiteren mit der CloudNotes Beispielanwendung rumspielen, und es scheint, dass NSFileWrappernicht inkrementelle Einsparung realisieren, zumindest in diesem Fall! Ich habe es getestet, indem ich ein Dokument mit 100 Notizen initialisiert habe, von denen jedes etwa 5 MB Daten enthielt. Ich habe hier und da eine kleine Änderung vorgenommen (eine einzelne Zeichenänderung in einer Textansicht markiert das Dokument als zu speichernd) und ungefähr aufgezeichnet, wie lange jede Speicherung dauerte. Der Test ist relativ grobes (und auf dem Simulator laufen), aber die Ergebnisse waren so etwas wie diese:

  • 1. schreiben: ~ 8000ms
  • 2. schreiben: ~ 4000ms
  • 3. schreiben: ~ 300ms
  • alle nachfolgenden schreibt: ~ 40ms

Offensichtlich gibt es viele Faktoren, die die Zeit, die beeinflussen, zumal es in einem Hintergrundthread Datei Koordination mit rettende, aber im allgemeinen die Tendenz scheint immer diese Art von exponen zu sein Verfall, bis alle Schreibvorgänge wirklich sehr schnell werden.

Aber ich versuche immer noch herauszufinden, warum das in meiner App nicht passiert. Bei einem großen mehrseitigen Dokument (groß, aber immer noch um ein Vielfaches kleiner als das oben für den CloudNotes-Test ausgeführte Dokument) kann der Benutzer viele Sekunden auf das Schließen eines Dokuments warten. Ich möchte keinen Spinner für etwas aufbieten müssen, das praktisch sofort sein sollte.

+0

Machst du eine automatische Speicherung oder speichst du alles auf einmal? Initiieren Sie das Speichern aus dem Hauptthread? Ersetzen Sie jeden NSFileWrapper oder nur die, die sich tatsächlich geändert haben? – Luke

+0

Ich verlasse mich ausschließlich auf das automatische Speichern, also werden die Sicherungen von UIDocument initiiert, indem man 'contentsForType: error:' (main thread) aufruft, bevor man schreibt (background thread). Ich ersetze nur die Datei-Wrapper, die ich geändert habe (_wenn sie sich ändern). Ich habe jedoch bemerkt, dass ich eine Methode verwende, die alle meine Seiten-Datei-Wrapper betrachtet und sie in ein geordnetes Array sortiert. Ich denke, dass dies mit dem faulen Laden von NSFileWrapper zusammenhängt und dazu führt, dass sie irgendwie schreiben müssen. Ich implementiere gerade einen Index, so dass ich das nicht machen muss und sehe, welchen Unterschied es macht. – Stuart

+0

Nein, es hat nicht geholfen, nur eine Indexdatei zu verwenden, um die Seiten/Seitenzahlen zu verfolgen. Ich versuche immer noch herauszufinden, was das langsame Speichern verursachen könnte. – Stuart

Antwort

2

NSFileWrapper lädt tatsächlich das gesamte Dokument in den Speicher. Mit einer UIDocument ist die Verwendung einer NSFileWrapper eigentlich nicht gut für große Dokumente. Die Dokumentation lässt Sie glauben, dass es inkrementelles Speichern tut, aber in meinem Fall schien es das nicht zu tun.

UIDocument ist nicht auf NSFileWrapper oder NSData beschränkt. Sie können Ihre eigene benutzerdefinierte Klasse verwenden, Sie müssen nur bestimmte Methoden überschreiben. Am Ende habe ich meine eigene Dateiwrapperklasse geschrieben, die einfach auf Dateien auf der Festplatte verweist und einzelne Dateien auf Anforderung liest/schreibt.

Dies ist, was meine UIDocument Klasse wie mit der Wrapper benutzerdefinierte Datei aussieht:

@implementation LSDocument 

- (BOOL)writeContents:(LSFileWrapper *)contents 
     andAttributes:(NSDictionary *)additionalFileAttributes 
      safelyToURL:(NSURL *)url 
    forSaveOperation:(UIDocumentSaveOperation)saveOperation 
       error:(NSError *__autoreleasing *)outError 
{ 
    return [contents writeUpdatesToURL:self.fileURL error:outError]; 
} 

- (BOOL)readFromURL:(NSURL *)url error:(NSError *__autoreleasing *)outError 
{ 
    __block LSFileWrapper *wrapper = [[LSFileWrapper alloc] initWithURL:url isDirectory:NO]; 
    __block BOOL result; 
    dispatch_sync(dispatch_get_main_queue(), ^(void) { 
     result = [self loadFromContents:wrapper 
           ofType:self.fileType 
            error:outError]; 
    }); 
    [wrapper loadCache]; 
    return result; 
} 

@end 

ich dies als Basisklasse verwenden und es für andere Projekte Unterklasse. Es sollte Ihnen eine Vorstellung davon geben, was Sie tun müssen, um eine benutzerdefinierte Dateiwrapperklasse zu integrieren.

+1

Danke, dass Sie sich die Zeit genommen haben, zu antworten (beide Fragen dazu!). Ich habe so viel Zeit damit verbracht, in die Details von NSFileWrapper zu schauen, aber es gibt so wenig Informationen über sie. Eine Reihe von Dingen verwirren mich: 1) Wenn Datei-Wrapper alle Daten in den Speicher laden und kein inkrementelles Speichern unterstützen, was ist der Sinn von ihnen? 2) Die Dokumentation macht nicht nur "Sie denken", sie machen inkrementelles Speichern, es sagt es direkt! Aus dem "Document-Based App Programmierhandbuch für iOS": "Datei-Wrapper unterstützen inkrementelles Speichern. Im Gegensatz zu einem einzelnen binären Datenobjekt, wenn eine Datei ... – Stuart

+1

... Wrapper enthält Ihre Dokumentdaten - z. B. Text und ein Bild - und der Text ändert sich, nur die Datei, die den Text enthält, muss auf die Festplatte geschrieben werden. Diese Fähigkeit führt zu einer besseren Leistung. " – Stuart

+1

Datei NSFileWrapper sind unveränderlich, also denke ich, dass es Datei-Wrapper *, die * ersetzt wurden *, speichern kann. Wenn Sie also einen NSFileWrapper austauschen, obwohl er geändert wurde, kann er trotzdem gespeichert werden. Ein NSFileWrapper lädt alles in den Speicher, also war das in meinem Fall nicht wünschenswert - also machte ich meine eigenen, die sich auf Dateien beziehen statt mit BOOL, um anzuzeigen, dass ich bestimmte Dateien im Speicher zwischenspeichern wollte. Wenn ich eine Datei aktualisiere, setzt sie einen "Inhalt" -Ivar mit einer "aktualisierten" BOOL. Wenn mein Elternteil gespeichert wird, sucht er nach "aktualisiert" und speichert die aktualisierten Dateien und setzt "Inhalt" auf Null, wenn der "Cache" BOOL falsch ist. – Luke