Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Core Data
Code

iOS 8: Kerndaten und asynchrones Abrufen

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Core Data from Scratch.
iOS 8: Core Data and Batch Updates

German (Deutsch) translation by Władysław Łucyszyn (you can also view the original English article)

Im vorherigen Artikel über iOS 8 und Core Data haben wir Batch-Updates besprochen.  Batch-Updates sind nicht die einzige neue API in der Stadt.  Ab iOS 8 und OS X Yosemite ist es möglich, Daten asynchron abzurufen.  In diesem Lernprogramm werden wir uns genauer ansehen, wie asynchrones Holen implementiert wird und in welchen Situationen Ihre Anwendung von dieser neuen API profitieren kann

1. Das Problem

Wie Batch-Updates steht das asynchrone Holen schon seit geraumer Zeit auf der Wunschliste vieler Entwickler.  Fetch-Anfragen können komplex sein und erfordern eine nicht unerhebliche Zeitspanne.  Während dieser Zeit blockiert die Abrufanforderung den Thread, auf dem sie ausgeführt wird, und blockiert als Ergebnis den Zugriff auf den Kontext des verwalteten Objekts, der die Abrufanforderung ausführt.  Das Problem ist einfach zu verstehen, aber wie sieht die Lösung von Apple aus?

2. Die Lösung 

Apples Antwort auf dieses Problem ist asynchrones Abrufen.  Eine asynchrone Abrufanforderung wird im Hintergrund ausgeführt.  Dies bedeutet, dass andere Aufgaben während der Ausführung nicht blockiert werden, z. B. das Aktualisieren der Benutzeroberfläche im Hauptthread. 

Asynchrones Abrufen bietet auch zwei weitere praktische Funktionen, Fortschrittsberichte und Stornierungen.  Eine asynchrone Abrufanforderung kann jederzeit abgebrochen werden, z. B. wenn der Benutzer entscheidet, dass die Abrufanforderung zu lange dauert.  Fortschrittsberichte sind eine nützliche Ergänzung, um dem Benutzer den aktuellen Status der Abrufanforderung anzuzeigen. 

Asynchrones Holen ist eine flexible API.  Es ist nicht nur möglich, eine asynchrone Abrufanforderung abzubrechen, es ist auch möglich, Änderungen an dem verwalteten Objektkontext vorzunehmen, während die asynchrone Abrufanforderung ausgeführt wird.  Mit anderen Worten, der Benutzer kann weiterhin Ihre Anwendung verwenden, während die Anwendung eine asynchrone Abrufanforderung im Hintergrund ausführt. 

3. Wie funktioniert es?

Wie Batch-Updates werden asynchrone Abrufanforderungen an den verwalteten Objektkontext als ein NSPersistentStoreRequest-Objekt weitergegeben, um genau zu sein, eine Instanz der NSAsynchronicFetchRequest-Klasse. 

Eine NSAsynchronousFetchRequest-Instanz wird mit einem NSFetchRequest-Objekt und einem Abschlussblock initialisiert.  Der Abschlussblock wird ausgeführt, wenn die asynchrone Abrufanforderung seine Abrufanforderung abgeschlossen hat. 

Lassen Sie uns die To-Do-Anwendung, die wir zuvor in dieser Serie erstellt haben, erneut aufrufen und die aktuelle Implementierung der NSFetchedResultsController-Klasse durch eine asynchrone Abrufanforderung ersetzen. 

Schritt 1: Projekteinrichtung

Laden oder klonen Sie das Projekt von GitHub und öffnen Sie es in Xcode 6.  Bevor wir mit derNSAsynchronousFetchRequest-Klasse arbeiten können, müssen wir einige Änderungen vornehmen.  Wir können die NSFetchedResultsController-Klasse nicht zum Verwalten der Daten der Tabellenansicht verwenden, da die NSFetchedResultsController-Klasse für die Ausführung im Hauptthread vorgesehen ist. 

Schritt 2: Ersetzen des abgerufenen Ergebniscontrollers 

Beginnen Sie mit der Aktualisierung der privaten Klassenerweiterung der TSPViewController-Klasse, wie unten gezeigt.  Wir entfernen die Eigenschaft "hetchedResultsController" und erstellen eine neue Eigenschaft namens "NSArray" zum Speichern der Aufgaben.  Dies bedeutet auch, dass die TSPViewController-Klasse nicht mehr dem NSFetchedResultsControllerDelegate-Protokoll entsprechen muss.

Bevor wir die Methode viewDidLoad umgestalten, möchte ich zuerst die Implementierung des UITableViewDataSource-Protokolls aktualisieren.  Sehen Sie sich die Änderungen an, die ich in den folgenden Codeblöcken vorgenommen habe. 

Wir müssen auch eine Codezeile in der prepareForSegue: sender: -Methode wie unten gezeigt ändern. 

Löschen Sie nicht zuletzt die Implementierung des NSFetchedResultsControllerDelegate-Protokolls, da wir es nicht mehr benötigen. 

Schritt 3: Erstellen der asynchronen Abrufanforderung 

Wie Sie unten sehen können, erstellen wir die asynchrone Abrufanforderung in der viewDidLoad-Methode des View-Controllers.  Lass uns einen Moment nehmen, um zu sehen, was vor sich geht. 

Zunächst erstellen und konfigurieren wir eine NSFetchRequest-Instanz, um die asynchrone Abrufanforderung zu initialisieren.  Es ist diese Abrufanforderung, bei der die asynchrone Abrufanforderung im Hintergrund ausgeführt wird.

Um eine NSAsynchronousFetchRequest-Instanz zu initialisieren, rufen wir initWithFetchRequest auf: completionBlock :, wobei fetchRequest und ein Completion-Block übergeben werden. 

Der Abschlussblock wird aufgerufen, wenn die asynchrone Abrufanforderung die Ausführung seiner Abrufanforderung abgeschlossen hat.  Der Completion-Block verwendet ein Argument vom Typ NSAsynchronousFetchResult, das das Ergebnis der Abfrage sowie einen Verweis auf die ursprüngliche asynchrone Abrufanforderung enthält. 

Im Completion-Block rufen wir processAsynchronousFetchResult: auf und übergeben das NSAsynchronousFetchResult-Objekt.  Wir sehen uns diese Hilfsmethode in einigen Augenblicken an. 

Die Ausführung der asynchronen Abrufanforderung ist fast identisch mit der Ausführung eines NSBatchUpdateRequest.  Wir rufen executeRequest auf: error: im Kontext des verwalteten Objekts, wobei die asynchrone Abrufanforderung und ein Zeiger auf ein NSError-Objekt übergeben werden.

Beachten Sie, dass wir die asynchrone Abrufanforderung durch Aufrufen von performBlock: im Kontext des verwalteten Objekts ausführen.  Obwohl dies nicht unbedingt notwendig ist, da die Methode viewDidLoad, in der wir die asynchrone Abrufanforderung erstellen und ausführen, im Hauptthread aufgerufen wird, ist es eine gute Angewohnheit und eine gute Vorgehensweise, dies zu tun. 

Obwohl die asynchrone Abrufanforderung im Hintergrund ausgeführt wird, beachten Sie, dass die executeRequest: error: -Methode sofort zurückkehrt und uns ein NSAsynchronousFetchResult-Objekt übergibt.  Sobald die asynchrone Abrufanforderung abgeschlossen ist, wird das gleiche NSAsynchronousFetchResult-Objekt mit dem Ergebnis der Abrufanforderung gefüllt. 

Schließlich prüfen wir, ob die asynchrone Abrufanforderung ohne Probleme ausgeführt wurde, indem überprüft wird, ob das NSError-Objekt gleich null ist.

Schritt 4: Verarbeitung des asynchronen Abrufergebnisses 

Die processAsynchronousFetchResult: -Methode ist nichts anderes als eine Hilfsmethode, in der wir das Ergebnis der asynchronen Abrufanforderung verarbeiten.  Wir setzen die items -Eigenschaft des View-Controllers mit dem Inhalt der finalResult -Eigenschaft des Ergebnisses und laden die Tabellenansicht neu. 

Schritt 5: Erstellen und Ausführen 

Erstellen Sie das Projekt und führen Sie die Anwendung im iOS-Simulator aus.  Sie werden möglicherweise überrascht sein, dass Ihre Anwendung abstürzt, wenn sie versucht, die asynchrone Abrufanforderung auszuführen.  Zum Glück sagt uns die Ausgabe in der Konsole, was schief gelaufen ist.

Wenn Sie den Artikel über Kerndaten und Nebenläufigkeit nicht gelesen haben, können Sie verwirrt sein, was Sie gerade lesen.  Denken Sie daran, dass Core Data drei Typen für den gemeinsamen Zugriff deklariert: NSConfinementConcurrencyType, NSPrivateQueueConcurrencyType und NSMainQueueConcurrencyType.  Wenn Sie einen verwalteten Objektkontext durch Aufrufen der Initmethode der Klasse erstellen, entspricht der Parallelitätstyp des resultierenden verwalteten ObjektkontextsNSConfinementConcurrencyType.  Dies ist der standardmäßige Nebenläufigkeitstyp.

Das Problem besteht jedoch darin, dass das asynchrone Abrufen nicht mit dem Typ NSConfinementConcurrencyType kompatibel ist.  Ohne zu sehr ins Detail zu gehen, ist es wichtig zu wissen, dass die asynchrone Abrufanforderung die Ergebnisse ihrer Abrufanforderung mit dem verwalteten Objektkontext zusammenführen muss, der die asynchrone Abrufanforderung ausgeführt hat.  Es muss wissen, in welcher Dispatch-Warteschlange dies möglich ist, und deshalb unterstützen nur NSPrivateQueueConcurrencyType und NSMainQueueConcurrencyType asynchrones Abrufen.  Die Lösung ist jedoch sehr einfach.

Schritt 6: Konfigurieren des verwalteten Objektkontexts 

Öffnen Sie TSPAppDelegate.m und aktualisieren Sie die Methode managedObjectContext wie unten gezeigt.

Die einzige Änderung, die wir vorgenommen haben, ist das Ersetzen der init-Methode durch initWithConcurrencyType :, wobei NSMainQueueConcurrencyType als Argument übergeben wird.  Dies bedeutet, dass auf den verwalteten Objektkontext nur vom Hauptthread aus zugegriffen werden darf.  Dies funktioniert gut, solange wir die performBlock: oder performBlockAndWait: -Methoden verwenden, um auf den Kontext des verwalteten Objekts zuzugreifen.

Führen Sie das Projekt noch einmal durch, um sicherzustellen, dass unsere Änderung das Problem tatsächlich behoben hat.

4. Zeige Fortschritt

Die NSAsynchronousFetchRequest-Klasse fügt Unterstützung hinzu, um den Fortschritt der Abrufanforderung zu überwachen, und es ist sogar möglich, eine asynchrone Abrufanforderung abzubrechen, z. B. wenn der Benutzer entscheidet, dass die Verarbeitung zu lange dauert.

Die NSAsynchronousFetchRequest-Klasse nutzt die NSProgress-Klasse zur Fortschrittsberichterstellung sowie zum Abbrechen einer asynchronen Abrufanforderung.  Die NSProgress-Klasse, die seit iOS 7 und OS X 10.9 verfügbar ist, ist eine clevere Möglichkeit, den Fortschritt einer Aufgabe zu überwachen, ohne die Aufgabe fest an die Benutzerschnittstelle koppeln zu müssen. 

Die NSProgress-Klasse unterstützt auch die Stornierung, wodurch eine asynchrone Abrufanforderung abgebrochen werden kann.  Lassen Sie uns herausfinden, was wir tun müssen, um die Fortschrittsberichterstattung für die asynchrone Abrufanforderung zu implementieren.

Schritt 1: Hinzufügen von SVProgressHUD 

Wir zeigen dem Benutzer den Fortschritt der asynchronen Abrufanforderung mithilfe der SVProgressHUD-Bibliothek von Sam Vermette an. Laden Sie die Bibliothek von GitHub herunter und fügen Sie den Ordner SVProgressHUD Ihrem Xcode-Projekt hinzu.

Schritt 2: Einrichten von NSProgress 

In diesem Artikel werden wir die NSProgress-Klasse nicht detailliert untersuchen, aber lesen Sie mehr dazu in der Dokumentation.  Wir erstellen eine NSProgress-Instanz in dem Block, den wir der performBlock: -Methode in der viewDidLoad-Methode des View-Controllers übergeben. 

Sie werden überrascht sein, dass wir die Gesamtzahl der Einheiten auf 1 setzen.  Der Grund ist einfach.  Wenn Core Data die asynchrone Abrufanforderung ausführt, weiß es nicht, wie viele Datensätze im persistenten Speicher gefunden werden.  Das bedeutet auch, dass wir dem Benutzer den relativen Fortschritt nicht anzeigen können - ein Prozentsatz.  Stattdessen zeigen wir dem Benutzer den absoluten Fortschritt - die Anzahl der gefundenen Datensätze. 

Sie können dieses Problem beheben, indem Sie eine Abrufanforderung ausführen, um die Anzahl der Datensätze abzurufen, bevor Sie die asynchrone Abrufanforderung ausführen.  Ich möchte dies jedoch nicht tun, da dies auch bedeutet, dass das Abrufen der Datensätze aus dem persistenten Speicher aufgrund der zusätzlichen Abrufanforderung zu Beginn länger dauert.

 Schritt 3: Hinzufügen eines Beobachters

Wenn wir die asynchrone Abrufanforderung ausführen, wird uns sofort ein NSAsynchronousFetchResult-Objekt übergeben.  Dieses Objekt hat eine Fortschrittseigenschaft vom Typ NSProgress.  Es ist diese Fortschrittseigenschaft, die wir beobachten müssen, wenn wir Fortschrittsupdates erhalten möchten.

Beachten Sie, dass wir resitCurrent für das Fortschrittsobjekt aufrufen, um den früheren Aufruf von becomeCurrentWithPendingUnitCount: auszugleichen.  Beachten Sie, dass diese beiden Methoden für denselben Thread aufgerufen werden müssen. 

Schritt 4: Entfernen des Beobachters

Im Abschlussblock der asynchronen Abrufanforderung entfernen wir den Beobachter und schließen das Fortschritts-HUD ab.

Bevor wir es umsetzen observeValueForKeyPath: ofObject: change: context :, wir müssen eine import-Anweisung für die SVProgressHUD-Bibliothek hinzufügen, die statische Variable ProgressContext deklarieren, die wir beim Hinzufügen und Entfernen des Beobachters als Kontext übergeben, und vor dem Erstellen des asynchronen Abrufs die Fortschritts-HUD anzeigen anfordern. 

Schritt 5: Fortschrittsbericht

Alles, was wir noch tun müssen, ist die Implementierung von observeValueForKeyPath: ofObject: change: context: method.  Wir prüfen, ob der Kontext ProgressContext entspricht, erstellen ein Statusobjekt, indem wir die Anzahl abgeschlossener Datensätze aus dem Änderungswörterbuch extrahieren, und aktualisieren die Fortschritts-HUD.  Beachten Sie, dass wir die Benutzeroberfläche im Hauptthread aktualisieren.

5. Dummy-Daten

Wenn wir unsere Anwendung richtig testen wollen, brauchen wir mehr Daten. Ich empfehle zwar nicht, den folgenden Ansatz in einer Produktionsanwendung zu verwenden, aber es ist eine schnelle und einfache Möglichkeit, die Datenbank mit Daten zu füllen. 

Öffnen Sie TSPAppDelegate.m und aktualisieren Sie die Anwendung: didFinishLaunchingWithOptions: Methode wie unten gezeigt.  Die populateDatabase-Methode ist eine einfache Hilfsmethode, in der Dummy-Daten zur Datenbank hinzugefügt werden.

Die Implementierung ist unkompliziert.  Da wir nur Dummy-Daten einmal einfügen möchten, überprüfen wir die Benutzerstandarddatenbank für den Schlüssel @ "didPopulateDatabase".  Wenn der Schlüssel nicht gesetzt ist, fügen wir Dummy-Daten ein.

Die Anzahl der Datensätze ist wichtig.  Wenn Sie die Anwendung auf dem iOS-Simulator ausführen möchten, können Sie 100.000 oder 1.000.000 Datensätze einfügen.  Dies funktioniert auf einem physischen Gerät nicht so gut und dauert zu lange.

In der for-Schleife erstellen wir ein verwaltetes Objekt und füllen es mit Daten.  Beachten Sie, dass die Änderungen des verwalteten Objektkontexts nicht bei jeder Iteration der for-Schleife gespeichert werden. 

Schließlich aktualisieren wir die Benutzerstandarddatenbank, um sicherzustellen, dass die Datenbank beim nächsten Start der Anwendung nicht ausgefüllt wird. 

Groß.  Führen Sie die Anwendung im iOS Simulator aus, um das Ergebnis zu sehen.  Sie werden feststellen, dass es einige Momente dauert, bis die asynchrone Abrufanforderung beginnt, Datensätze abzurufen und die Fortschritts-HUD zu aktualisieren.

6. Brechen von Änderungen

Durch Ersetzen der abgerufenen Ergebnis-Controller-Klasse durch eine asynchrone Abrufanforderung haben wir einige Teile der Anwendung abgebrochen.  Zum Beispiel scheint das Antippen des Häkchens eines Aufgabenobjekts nicht mehr zu funktionieren.  Während die Datenbank aktualisiert wird, spiegelt die Benutzeroberfläche die Änderung nicht wider.  Die Lösung ist relativ einfach zu beheben und ich überlasse es Ihnen, eine Lösung zu implementieren.  Sie sollten jetzt genügend Kenntnisse haben, um das Problem zu verstehen und eine geeignete Lösung zu finden.

Fazit

Ich bin sicher, Sie stimmen zu, dass das asynchrone Holen überraschend einfach ist.  Das Heavy-Lifting wird von Core Data durchgeführt, so dass die Ergebnisse der asynchronen Abrufanforderung nicht manuell mit dem Kontext des verwalteten Objekts zusammengeführt werden müssen.  Ihre einzige Aufgabe besteht darin, die Benutzeroberfläche zu aktualisieren, wenn die asynchrone Abrufanforderung Ihnen ihre Ergebnisse übermittelt.  Zusammen mit Batch-Updates ist es eine großartige Ergänzung zum Core Data Framework. 

Dieser Artikel schließt auch diese Serie über Core Data ab.  Sie haben viel über das Core-Data-Framework gelernt und Sie kennen alle Grundlagen, um Core Data in einer realen Anwendung zu verwenden.  Core Data ist ein leistungsfähiges Framework und mit der Veröffentlichung von iOS 8 hat Apple uns gezeigt, dass es jedes Jahr besser wird.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.