Einführung: Warum Kerndaten und NSFetchedResultsController wichtig sind

In der modernen iOS-Entwicklung hängt die Bereitstellung einer flüssigen, reaktionsschnellen Benutzeroberfläche oft davon ab, wie effizient Ihre App Daten verarbeitet, die sich im Laufe der Zeit ändern. Ob Sie einen Social Feed, einen Task-Manager oder ein Inventarsystem erstellen, die den Benutzern angezeigten Daten sind selten statisch. Core Data - Apples ausgereiftes Objektgraphen- und Persistenz-Framework - gepaart mit (FRC) bietet eine kampferprobte Lösung für die Verwaltung dynamischer, groß angelegter Datensätze, während die Benutzeroberfläche ohne manuellen Overhead synchronisiert bleibt.

Dieser Artikel bietet eine ausführliche, praktische Anleitung zur Verwendung von Kerndaten mit -Benachrichtigungen. Wenn Objekte eingefügt, aktualisiert oder gelöscht werden, sendet der Kontext diese Änderungen, was genau das ist, was nutzt, um Ihre Benutzeroberfläche konsistent zu halten.

Für die offizielle Entwicklerdokumentation siehe Apple’s Core Data Framework Reference.

NSFetchedResultController: Die Brücke zwischen Core Data und Ihrer Benutzeroberfläche

ist ein Controller-Objekt, das entwickelt wurde, um die von einer Core Data-Abrufanforderung zurückgegebenen Ergebnisse effizient zu verwalten, insbesondere wenn erwartet wird, dass sich die zugrunde liegenden Daten ändern.

Hauptmerkmale

  • Automatisches Change Tracking: Der FRC hört Kontextbenachrichtigungen und übersetzt sie in strukturierte Delegierten-Callbacks (, , ).
  • In Abschnitten eingebaut: Durch die Angabe eines s holen die Controllergruppen Ergebnisse in Abschnitte ab, wodurch es trivial ist, geschnittene Tabellenansichten oder Sammelansichten anzuzeigen.
  • Performance Optimization: Der FRC verwendet Fehler und Caching unter der Haube. Er holt nur Daten nach Bedarf ab und kann optional einen persistenten Cache verwenden, um ein erneutes Abrufen zu vermeiden, wenn der verwaltete Objektkontext gespeichert wird.

Methoden im Detail delegieren

Um vom Controller voll profitieren zu können, müssen Sie das implementieren. Das häufigste Muster ist, diese Rückrufe innerhalb eines oder -Delegierten zu verwenden, um die Benutzeroberfläche zu aktualisieren.

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
 tableView.beginUpdates()
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
 didChange anObject: Any,
 at indexPath: IndexPath?,
 for type: NSFetchedResultsChangeType,
 newIndexPath: IndexPath?) {
 switch type {
 case .insert:
 tableView.insertRows(at: [newIndexPath!], with: .fade)
 case .delete:
 tableView.deleteRows(at: [indexPath!], with: .fade)
 case .update:
 tableView.reloadRows(at: [indexPath!], with: .fade)
 case .move:
 tableView.moveRow(at: indexPath!, to: newIndexPath!)
 @unknown default:
 tableView.reloadData()
 }
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
 tableView.endUpdates()
}

Dieses Muster stellt sicher, dass die Tabellenansicht Änderungen synchron mit den zugrunde liegenden Daten animiert und so ein Flimmern oder Inkonsistenz verhindert.

Schritt-für-Schritt-Implementierung

Unten ist ein vollständiges, produktionsbereites Beispiel mit Swift 5, das auf iOS 15+ abzielt. Wir nehmen eine einfache Entität namens mit Attributen (String) und (Bool) und ein (Datum) an.

1. Einrichten des Core Data Stack

In Ihrem oder einem dedizierten (in SwiftUI-Apps üblich) erstellen Sie den persistenten Container:

class PersistenceController {
 static let shared = PersistenceController()
 let container: NSPersistentContainer

 init() {
 container = NSPersistentContainer(name: "YourModelName")
 container.loadPersistentStores { storeDescription, error in
 if let error = error as NSError? {
 fatalError("Unresolved error \(error), \(error.userInfo)")
 }
 }
 container.viewContext.automaticallyMergesChangesFromParent = true
 }
}

2. Erstellen Sie die NSManagedObject Subclass

Verwenden Sie Xcodes Datenmodell-Editor, um die Klassendateien zu generieren oder manuell zu erstellen.

3. Konfigurieren Sie den Fetch Request und FRC

Richten Sie in Ihrem View-Controller die Fetch-Anfrage ein und initialisieren Sie den Fetched Results Controller.

lazy var fetchedResultsController: NSFetchedResultsController<Task> = {
 let fetchRequest: NSFetchRequest<Task> = Task.fetchRequest()
 let sortDescriptor = NSSortDescriptor(key: "dueDate", ascending: true)
 fetchRequest.sortDescriptors = [sortDescriptor]
 // Optional: limit results with batch size for large datasets
 fetchRequest.fetchBatchSize = 20

 let controller = NSFetchedResultsController(
 fetchRequest: fetchRequest,
 managedObjectContext: PersistenceController.shared.container.viewContext,
 sectionNameKeyPath: "completionStatus", // e.g., a transient attribute or a computed property
 cacheName: nil
 )
 controller.delegate = self
 try? controller.performFetch()
 return controller
}()

4. Fahren Sie die Tischansicht mit dem FRC

Ihre Methoden werden trivial:

func numberOfSections(in tableView: UITableView) -> Int {
 fetchedResultsController.sections?.count ?? 0
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
 let sectionInfo = fetchedResultsController.sections![section]
 return sectionInfo.numberOfObjects
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
 let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath)
 let task = fetchedResultsController.object(at: indexPath)
 configure(cell, with: task)
 return cell
}

Beachten Sie, dass wir niemals manuell aufrufen - die Delegiertenmethoden behandeln jedes Einfügen, Löschen und Aktualisieren.

Advanced Usage und Best Practices

Gewinde Sicherheit

Core Data-Kontexte sind nicht threadsicher. Verwenden Sie immer die (die in der Hauptwarteschlange läuft) für alle UI-bezogenen Abrufanforderungen und FRCs. Für Hintergrundarbeit erstellen Sie einen privaten Warteschlangenkontext und führen Änderungen am Ansichtskontext zusammen. Vermeiden Sie die Verwendung derselben FRC über mehrere Warteschlangen hinweg.

Caching

Der Parameter kann die Startleistung verbessern, indem er Abschnitts- und Objektinformationen fortsetzt.

Umgang mit großen Datensätzen

Wenn Sie verwenden, um die Anzahl der Objekte zu begrenzen, die in den Speicher geholt werden.

Integration mit SwiftUI

Während UIKit-zentriert ist, können Sie es immer noch in SwiftUI verwenden, indem Sie es in einen einwickeln oder den neueren -Eigenschafts-Wrapper verwenden (der intern einen ähnlichen Mechanismus verwendet).

Häufige Fallstricke und wie man sie vermeidet

  • Vergessen : Der Controller wird den Fetch erst ausführen, wenn Sie diese Methode aufrufen.
  • Delegiert nicht setzen: Ohne einen Delegaten werden Änderungen nicht in die Tabellenansicht übertragen.
  • Falsche Sortierdeskriptoren: Wenn der Schlüsselpfad für den Abschnittsnamen nicht mit dem ersten Sortierdeskriptor übereinstimmt, werden Abschnitte möglicherweise in der Reihenfolge angezeigt.
  • Wenn Sie neben FRC: auch anrufen, während der FRC Änderungen animiert, kann dies zu Abstürzen führen.
  • Das Ignorieren von Kontextspeicherfehlern: Wenn Sie den Kontext speichern und ein Fehler auftritt, wird der FRC möglicherweise nicht benachrichtigt.

Leistungsbetrachtungen

ist bereits effizient, aber hier sind zusätzliche Optimierungen:

  • Verwenden Sie Prädikate mit Bedacht: Ein komplexes Prädikat kann den anfänglichen Abruf verlangsamen. Verwenden Sie, wenn möglich, indizierte Attribute.
  • Limit fetched properties: Wenn du nur bestimmte Attribute brauchst, setze auf die Abrufanforderung.
  • Batch-Updates: Wenn Sie viele Änderungen vornehmen, wickeln Sie sie in einen -Block, um die Anzahl der Delegierten-Callbacks zu reduzieren.
  • Vermeide unnötige Fehler: Wenn du weißt, dass du auf alle Objekte in einem Ergebnissatz zugreifst, benutze , um sie vorab abzurufen, aber sei vorsichtig mit dem Gedächtnis.

Für einen tieferen Einblick in die Core Data Performance siehe Apples Core Data Performance Guide.

Beispiele aus der realen Welt

Beispiel 1: Chat-Anwendung

Eine Nachrichten-App zeigt eine Liste von Gesprächen an, die nach der letzten Nachricht sortiert sind. Neue eingehende Nachrichten sollten sofort erscheinen. Mit einer FRC kann die Kontaktansicht Änderungen nur für die Konversationseinheiten des aktuellen Benutzers abonnieren.

Beispiel 2: Bestandsverwaltung

Eine E-Commerce-App zeigt Produkte in Kategorien an. Ändert sich der Bestand von einer Hintergrundsynchronisierung, aktualisiert der FRC automatisch die Benutzeroberfläche. Durch die Einstellung von auf 50 bleibt die Ansicht auch bei Tausenden von Artikeln reaktionsschnell.

Beispiel 3: To-Do-Liste mit Kategorien

Das klassische Beispiel: Aufgaben gruppiert in "Overdue", "Today" und "Upcoming". Das kann ein vorübergehendes abgeleitetes Attribut sein, das aus und aktuellem Datum berechnet wird.

Schlussfolgerung

Die Kombination von Core Data und FLT:47 zu beherrschen, ist ein Eckpfeiler für die Erstellung robuster, dynamischer iOS-Anwendungen. Indem Sie die starke Aufhebung des Change-Trackings und der UI-Synchronisierung auf Apples Frameworks auslagern, können Sie sich auf die Schaffung einer großartigen Benutzererfahrung konzentrieren, anstatt Datenmanagement-Code auf der Boilerplate-Plattform zu schreiben.

Ob Sie eine ältere UIKit-App pflegen oder SwiftUI mit übernehmen, die Prinzipien bleiben die gleichen: Verstehen Sie Ihren Objektgraphen, konfigurieren Sie die Abrufanforderungen sorgfältig und lassen Sie den abgeholten Ergebniscontroller das tun, was er am besten kann. Mit den in diesem Artikel beschriebenen Praktiken - einschließlich Caching, Thread-Sicherheit und Performance-Tuning - sind Sie gut gerüstet, um jeden Datensatz zu verarbeiten, der sich im Laufe der Zeit entwickelt.

Für weitere Erkundungen lesen Sie Ray Wenderlichs Core Data Tutorial und den NSHipster Artikel über NSFetchedResultsController.