2012-08-03 10 views
5

In meiner App habe ich einen "Master" NSPrivateQueueConcurrencyType Kontext, der als übergeordnetes Element zu einem NSMainQueueConcurrencyType Kontext dient, auf den sich die View-Controller verlassen und weitergeben. Ich wollte dies tun, um Async-Speicher zu nutzen (diese App verwendet iCloud mit Core-Daten und spart viel Arbeit beim Exportieren von Ubiquity-Logs). Der Aufbau ist ähnlich den Zarra Ansatz am unteren Rande erwähnt von this articleLangsam löscht und speichert in verschachtelten NSManagedObjectContext

Speichert in der App sieht typischerweise wie:

[context save:nil]; 
[context.parentContext performBlock:^{ 
    [context.parentContext save:nil]; 
}]; 

Dies scheint für kleine Änderungen/Updates gut zu funktionieren, aber ich bin verwirrt über was ich sehe, wenn ich viele Objekte lösche (z. B. ein Projekt löschen, das hunderte von Aufgabenobjekten enthält).

In dieser Situation ist das Speichern asynchron, aber es sieht so aus, als ob der Haupt-Thread in eine Semaphor-Wartesituation gerät (wie ich sehe, wenn ich den Debugger pausiere) und effektiv blockiert wird (ich kann nicht in der UI blättern) bis zum Hintergrund privater Kontext beendet das Speichern.

Tatsächlich habe ich gerade bemerkt, dass Swipe-to-Delete-Löschungen eines einzelnen Objekts trotz dieses Async-Speichermusters eine merkliche Verzögerung verursachen, so scheint es, dass alle Löschungen in der App langsam sind.

Wenn ich alternativ einen neuen NSPrivateQueueConcurrencyType Kontext zuweisen, seinen persistenten Speicherkoordinator auf den Haupt-PSC im AppDelegate setzen und das Löschen und Speichern durchführen, macht es immer noch viel Arbeit, aber die Benutzeroberfläche wird nie blockiert (aber dann muss ich mir Sorgen machen, die Kontexte zu koordinieren, und zwar im Hauptkontext.

Irgendwelche Ideen, was ich falsch gemacht haben könnte, oder haben Sie dieses Verhalten auch gesehen?

Antwort

8

Löschvorgänge können lange dauern. Sie müssen alle Beziehungen wiederherstellen. Es gibt mehrere Dinge, die Sie tun sollten, wenn Sie große Löschungen vornehmen.

Erstens, warum blockiert Ihre Benutzeroberfläche, wenn die Löschvorgänge im Hintergrund stattfinden? Weil dieser Hintergrund-Thread wirklich mit dem Löschen des Blocks beschäftigt ist und Ihre Benutzeroberfläche versucht, die Daten zu aktualisieren, während das Löschen stattfindet.

Der Thread, der die eigentliche Arbeit ausführt, wird von einer FIFO-Warteschlange verwaltet. Wenn Sie also eine große auszuführende Aufgabe eingeben, kann er erst nach Abschluss der langen Ausführung andere Aufgaben ausführen.

Sie verwenden wahrscheinlich einen FRC und versuchen Aktualisierungen zu holen, wenn sich Objekte und Beziehungen ändern.

Wenn Sie ein großes Löschen haben, möchten Sie möglicherweise den Abruf ändern, damit nur das aktuelle MOC nach Daten durchsucht wird, während das Löschen stattfindet. Wenn der Löschvorgang abgeschlossen ist, können Sie der Abrufanforderung mitteilen, dass die Abfrage bis zum Speicher selbst zurückgehen soll.

Auch wenn Sie eine Menge Zeug löschen, ist es am besten, es in kleinen Blöcken innerhalb eines Autorelease-Pools in einem separaten Thread zu machen. Von diesem Hintergrund Thread, löschen Sie kleine Stücke auf einmal, und tun Sie es mit performBlockAndWait. Auf diese Weise laden Sie die Warteschlange des Arbeitsthreads nicht mit Löschanforderungen, und Ihr UI-Thread kann seine Anforderungen verarbeiten. schneller.

Alternativ können Sie auch die erweiterten Funktionen von GCD verwenden, um eine Warteschlange mit hoher Priorität und niedriger Priorität zu haben, die verwendet wird, um dem Worker-Thread Arbeit zuzuführen. Sie können Ihre Schreibvorgänge in die Warteschlange mit niedriger Priorität und Ihre Lesevorgänge in die Warteschlange mit hoher Priorität versetzen.

+0

Danke Jody!Ich stoße ständig auf deine hilfreichen Antworten. Ich mache noch etwas mehr Arbeit, um meine Probleme zu untersuchen. Auf einem Bildschirm präsentiere ich meine Projekte (Projekte haben viele Aufgaben) und verwenden keine NSFRC. Ich schaue mir die Dinge an, die du für meinen anderen Bildschirm erwähnt hast (der die Aufgaben auflistet und einen NSFRC verwendet), wo der Swipe-to-Delete eines einzelnen Tasks langsam ist und den verschachtelten Ansatz verwendet (und schnell, wenn ich zum Standard zurückkehre) NSMainQueueConcurrencyType-Kontext mit der PSC verbunden) – azsromej

+0

Führen Sie es in Instrumenten. Das ist der beste Weg, um genau zu sehen, was passiert. –

+0

Seltsamerweise führt das Verwenden von Swipe-to-Delete zu einem Speichervorgang, der die Benutzeroberfläche blockiert, aber mit der Schaltfläche Bearbeiten-Fertig, durch Antippen des roten Kreises und anschließend Löschen wird nicht blockiert, obwohl beide commitEditingStyle durchlaufen: forRowAtIndexPath (deleteObject: und speichern: passieren) – azsromej

Verwandte Themen