Core Data Performance: 6 Tipps, die Sie kennen sollten

Das Schreiben von Core Data-Code mit Blick auf die Leistung hilft, Ihre App für die Zukunft vorzubereiten. Ihre Datenbank mag am Anfang klein sein, kann aber leicht wachsen, was zu langsamen Abfragen und weniger Erfahrung für den Benutzer führt.

Seit ich 2017 mit dem Schreiben der Collect by WeTransfer-App begonnen habe, schreibe ich eine Menge Code für Core Data und berühre ihn fast jeden Tag. Mit Millionen von Benutzern, die viele Inhalte hinzufügen, ist das Ausführen von Code im Zusammenhang mit Kerndaten zu einer wichtigen Fähigkeit in unserem Team geworden.

Architektur von SwiftUI-Apps mit MVC und MVVMAlobwohl Sie eine App erstellen können, indem Sie einfach Code zusammenwerfen, ohne Best Practices und eine robuste Architektur, werden Sie bald mit unüberschaubarem Spaghetti-Code enden. In diesem kostenlosen Handbuch erfahren Sie, wie Sie solide und wartbare Apps mit weniger Fehlern erstellen.

Im Laufe der Jahre haben wir viele Erkenntnisse entwickelt, die ich gerne mit Ihnen durch 5 Tipps teile, die Sie kennen sollten.

1: Verwenden Sie einen Hintergrundkontext für verwaltete Objekte

Eine Sache, die wir von Anfang an nicht getan haben, ist die Verwendung eines Hintergrundkontexts für verwaltete Objekte. Wir haben den Ansichtskontext nur verwendet, um kerndatenbezogene Aufgaben auszuführen: Einfügen neuer Inhalte, Löschen von Inhalten, Abrufen von Inhalten usw.

Am Anfang war unsere App relativ klein. Die ausschließliche Verwendung des Ansichtskontexts war kein wirkliches Problem und führte zu keinen sichtbaren Leistungseinbußen im Zusammenhang mit Kerndaten. Sobald unsere App zu wachsen begann, stellten wir natürlich fest, dass der Ansichtskontext mit der Hauptwarteschlange verknüpft war. Langsame Abfragen blockierten unsere Benutzeroberfläche und unsere App reagierte weniger.

Im Allgemeinen empfiehlt es sich, die Datenverarbeitung in einer Hintergrundwarteschlange durchzuführen, da dies CPU-intensiv sein kann. Beispiele wie das Importieren von JSON in Core Data könnten andernfalls den Ansichtskontext blockieren und zu einer nicht reagierenden Benutzeroberfläche führen.

Die Lösung besteht darin, einen hintergrundverwalteten Objektkontext zu verwenden. Mit den neuesten APIs können Sie ganz einfach einen neuen Kontext aus Ihrem persistenten Container erstellen:

let backgroundContext = persistentContainer.newBackgroundContext()

Ich empfehle diese Methode gegenüber dem NSManagedObjectContext(concurrenyType:) Initialisierer, da sie automatisch mit dem NSPersistentStoreCoordinator verknüpft wird und auch NSManagedObjectContextDidSave Broadcasts verbraucht. Dadurch wird Ihr Hintergrundkontext mit dem Ansichtskontext synchronisiert.

Sie können diesen Hintergrundkontext in einer benutzerdefinierten persistenten Container-Unterklasse speichern. Auf diese Weise können Sie Ihren Hintergrundkontext wiederverwenden und müssen nur zwei Kontexte verwalten. Dies hält Ihre Kerndatenstruktur einfach zu verstehen und verhindert, dass mehrere nicht synchrone Kontexte vorhanden sind.

Wenn Sie den Hintergrundkontext nur an wenigen Stellen verwenden müssen, können Sie auch die Methode performBackgroundTask(_:) verwenden, die einen Hintergrundkontext erstellt:

persistentContainer.performBackgroundTask { (backgroundContext) in // .. Core Data Code}

Diese Methode erstellt jedoch bei jedem Aufruf eine neue NSManagedObjectContext . Möglicherweise möchten Sie den freigegebenen Hintergrundkontext verwenden, wenn Sie häufiger an einen Hintergrundkontext senden.

Wichtig: Übergeben Sie keine NSManagedObject-Instanzen zwischen Warteschlangen

Das Schreiben von Multithread-Core-Data-Code ist viel komplexer als die Verwendung eines einzelnen Ansichtskontexts. Der Grund dafür ist, dass Sie ein NSManagedObject instanziiertes NSManagedObject aus einem Ansichtskontext nicht einfach an einen Hintergrundkontext übergeben können. Dies würde zu einem Absturz und einer möglichen Datenbeschädigung führen.

Wenn ein verwaltetes Objekt von einer Warteschlange in eine andere verschoben werden muss, können Sie NSManagedObjectID verwenden, das threadsicher ist:

let managedObject = NSManagedObject(context: persistentContainer.viewContext)backgroundContext.perform { let object = try? backgroundContext.existingObject(with: managedObject.objectID)}

2: Speichern Sie einen verwalteten Objektkontext nur bei Bedarf

Beim Speichern eines verwalteten Objektkontexts werden alle aktuellen Änderungen in den übergeordneten Speicher des Kontexts übernommen. Wie Sie sich vorstellen können, ist dies keine billige Operation und sollte nur bei Bedarf verwendet werden, um die Leistung in Core Data sicherzustellen.

Zuallererst ist es wichtig zu überprüfen, ob es überhaupt etwas zu speichern gibt. Wenn keine Änderungen festgeschrieben werden müssen, gibt es auch keinen Grund, ein Speichern durchzuführen. Indem Sie eine saveIfNeeded -Methode erstellen, können Sie einfach eine Überprüfung dafür einbauen:

Überlegen Sie sorgfältig, wann Sie Ihre Änderungen speichern sollen

Abgesehen von der Verwendung von saveIfNeeded anstelle von save() müssen Sie auch überlegen, ob ein Speichern sinnvoll ist. Obwohl ein Kontext Änderungen enthalten kann, ist es nicht immer erforderlich, diese Änderungen direkt zu übernehmen.

Wenn Sie beispielsweise mehrere Elemente in Ihre Datenbank importieren, möchten Sie sie möglicherweise erst speichern, nachdem Sie alle Elemente in Ihren Hintergrundkontext importiert haben. Auf ein Speichern folgen häufig UI-Updates, und mehrere Speicherungen nacheinander können leicht zu unnötigen Neuladungen führen. Berücksichtigen Sie außerdem, dass gespeicherte Änderungen in einem Hintergrundkontext mit dem Ansichtskontext zusammengeführt werden, wodurch auch die Hauptwarteschlange kurzzeitig blockiert wird. Deshalb sei bewusst!

3: Nur holen, was Sie brauchen

Das Abrufen von Daten ist eine teure Aufgabe und muss so performant wie möglich sein, um Ihre App für große Datensätze vorzubereiten. Der folgende Code ist ein häufig gemachter Fehler:

Dieser Code lädt alle eingefügten Objekte in den Speicher, während er direkt danach gefiltert wird, um nur mit Inhalten mit einem Namen zu verbleiben.

Es ist viel performanter, Prädikate zu verwenden, um nur die Objekte abzurufen, die benötigt werden. Der obige Filter kann wie folgt geschrieben werden: NSPredicate:

Dies hat zwei Vorteile:

  • Nur die benötigten Objekte werden in den Speicher geladen
  • Sie müssen nicht über alle Objekte iterieren

Prädikate sind sehr flexibel und sollten es Ihnen ermöglichen, den gewünschten Datensatz in den meisten Fällen abzurufen, während die Leistung in Core Data erhalten bleibt.

4: Verwenden Sie Abruflimits

Im Anschluss an das vorherige Beispiel ist es wichtig, Abruflimits festzulegen, wenn Sie nur einen Teil des Datensatzes anzeigen möchten.

Angenommen, Sie benötigen nur die ersten 3 Namen aller Inhaltselemente. In diesem Fall wäre es nicht erforderlich, alle Inhaltselemente mit einem Namen in den Speicher zu laden. Wir könnten dies verhindern, indem wir ein Abruflimit festlegen:

Dieser Code gibt nur die ersten 3 Inhaltselemente mit einem Namen zurück.

5: Löschen Sie viele Objekte gleichzeitig mit einem NSBatchDeleteRequest

Anstatt über ein Dataset zu iterieren und jedes Objekt einzeln zu löschen, ist es oft performanter, ein NSBatchDeleteRequest zu verwenden, das schneller ausgeführt wird, da es auf SQL-Ebene im persistenten Speicher selbst ausgeführt wird.

Weitere Informationen zum Löschen von Stapelanforderungen finden Sie in meinem Blogbeitrag Mit NSBatchDeleteRequest zum Löschen von Stapeln in Core Data.

6: Wissen, wie man Core Data Code debuggt

Wie bei jedem Code, den Sie schreiben, ist es wichtig zu wissen, wie man ihn optimiert und debuggt, wenn er nicht wie erwartet funktioniert. Es gibt viele Möglichkeiten zum Debuggen, die am besten in meinem speziellen Blogbeitrag erläutert werden: Core Data Debugging in Xcode using launch arguments .

Architektur von SwiftUI-Apps mit MVC und MVVMAlobwohl Sie eine App erstellen können, indem Sie einfach Code zusammenwerfen, ohne Best Practices und eine robuste Architektur, werden Sie bald mit unüberschaubarem Spaghetti-Code enden. In diesem kostenlosen Handbuch erfahren Sie, wie Sie solide und wartbare Apps mit weniger Fehlern erstellen.

Fazit

Wenn Sie von Anfang an performanten Core Data-Code schreiben, können Sie Ihre App für zukünftige große Datensätze vorbereiten. Obwohl Ihre App zu Beginn möglicherweise eine gute Leistung erbringt, kann sie sich leicht verlangsamen, sobald Ihre Datenbank und Ihr Modell wachsen. Durch die Verwendung eines Hintergrundkontexts, intelligenter Abrufanforderungen und Stapellöschanforderungen wird Ihr Kerndatencode bereits leistungsfähiger.

Wenn Sie Ihr Swift-Wissen noch weiter verbessern möchten, besuchen Sie die Swift-Kategorieseite. Fühlen Sie sich frei, mich zu kontaktieren oder mir auf Twitter zu twittern, wenn Sie zusätzliche Tipps oder Feedback haben.

Danke!



+