Was ist ein Kerndatenfehler?

Fehler sind ein wesentlicher Bestandteil von Core Data. Auch wenn der Begriff bedrohlich klingt, sind Fehler dem Lebenszyklus eines Kerndatensatzes inhärent. In diesem Lernprogramm erfahren Sie, was Fehler sind, wie sie gehandhabt werden und wie Sie Fehler beheben können.

Voraussetzungen

Core Data ist ein fortgeschrittenes Thema. Ich gehe davon aus, dass Sie bereits mit der Xcode- und iOS-Entwicklung vertraut sind. Obwohl ich für dieses Tutorial die Programmiersprache Swift verwende, ist alles in diesem Tutorial auch auf Objective-C anwendbar.

Wat ist ein Fehler?

Core Data kann dank der harten Arbeit des Core Data-Teams von Apple sehr gut arbeiten. Core Data ist stark optimiert, um seinen Speicherbedarf gering zu halten, ohne die Leistung zu beeinträchtigen. Fehler ist eine der Techniken, die Core Data verwendet, um so wenig Speicher wie möglich zu verbrauchen.

Fehler sind nicht nur für Core Data relevant. Eine ähnliche Technik wird in vielen anderen Frameworks wie Ember und Ruby on Rails verwendet. Die Idee ist einfach: Laden Sie Daten nur dann, wenn sie benötigt werden. Damit Fehler funktionieren, zaubert Core Data ein wenig Magie unter der Haube, indem er zur Kompilierzeit benutzerdefinierte Unterklassen erstellt, die die Fehler darstellen. Mal sehen, wie das an einem Beispiel funktioniert.

Ich habe eine einfache Beispielanwendung erstellt, mit der Sie arbeiten können. Laden Sie das Xcode-Projekt von GitHub herunter und öffnen Sie es in Xcode. Das Projekt verwendet Swift 2.1, dh Sie benötigen Xcode 7.1 oder höher, um den Compiler zufrieden zu stellen.

Das Datenmodell enthält zwei Entitäten, Liste und Artikel. Eine Liste kann null oder mehr Elemente enthalten, und ein Element ist immer mit einer Liste verknüpft. Es ist ein klassisches Beispiel für eine Eins-zu-Viele-Beziehung.

Führen Sie die Anwendung aus, und füllen Sie den persistenten Speicher, eine SQLite-Datenbank, mit einigen Daten auf, indem Sie einige Listen und Elemente erstellen. Beenden Sie die Anwendung, wenn Sie fertig sind.

Fehler beim Abfeuern

Sie sollten jetzt eine Anwendung mit einigen Daten haben. Lassen Sie uns sehen, wie Fehler in der Praxis funktionieren, indem Sie einige Druckanweisungen hinzufügen. Öffnen ListsViewController.swift und suchen prepareForSegue (_: Absender :). In dieser Methode rufen wir die Liste ab, die der Benutzer in der Tabellensicht ausgewählt hat. Kommentieren Sie die Druckanweisungen in prepareForSegue (_: Absender :) wie in der Implementierung unten gezeigt.

func preparForSegue überschreiben (segue: UIStoryboardSegue, Sender: AnyObject?) if segue.identifier == SegueListViewController guard let indexPath = tableView.indexPathForSelectedRow else return // Abrufliste Listendruck ("1: \ (Liste)"), wenn Elemente = List.items Print ("2: \ (Artikel)") gedruckt werden sollen ("3: \ (Artikel.Zahl)"). Drucken ("4: \ (items) ") wenn let item = items.anyObject () print (" 5: \ (item) ") print (" 6: \ (item.name) ") print (" 7: \ (item) ")  print ("8: \ (list)") // Zielsuchcontroller abrufen lassen listViewController = segue.destinationViewController als! ListViewController // View Controller konfigurieren listViewController.list = list

Führen Sie die Anwendung aus und tippen Sie im Listenansicht-Controller auf eine der Listen. Das Beispiel ist nur interessant, wenn Sie auf eine Liste tippen, der ein oder mehrere Elemente zugeordnet sind. Lassen Sie uns die Ausgabe der Druckanweisungen Schritt für Schritt überprüfen.

1:  (Entität: Liste; ID: 0xd0000000001c0000  ; Daten: items = ""; name =" Liste 6 ";)

Als Erstes ist der Klassenname zu beachten, Fehler.Liste. Das erwarten wir seit dem Namen des Moduls Fehler und die Klasse wird benannt Liste. In Swift setzt sich der vollständige Klassenname einer Klasse aus dem Namen des Moduls und dem Namen der Klasse zusammen.

Die Ausgabe in der Konsole zeigt auch den Namen der Entität an, Liste, und ein Wörterbuch mit Daten. Das Name Attribut ist auf gesetzt Liste 6 während Artikel Attribut ist als gekennzeichnet Beziehungsfehler. Dies ist die erste Art von Fehler in Core Data. Core Data weiß, dass die Beziehung nicht geladen werden muss. Stattdessen wird die Beziehung als Fehler bezeichnet. Die zweite Print-Anweisung bestätigt dies, wie Sie unten sehen können.

2: Beziehungsfehler "Artikel" bei verwaltetem Objekt (0x154e5d0b0)  (Entität: Liste; ID: 0xd0000000001c0000  ; Daten: items = ""; name =" Liste 6 ";)

Die dritte gedruckte Aussage ist jedoch interessanter. Es gibt die Anzahl der Elemente aus, die der Liste zugeordnet sind.

3: 2

Core Data kann dies nur durch Auslösen des Beziehungsfehlers tun. Der permanente Speicher wird nach den Elementen gefragt, die der Liste zugeordnet sind. Dies ist jedoch nur ein Teil der Geschichte, wie die vierte Druckaussage veranschaulicht.

4: Beziehungselemente für verwaltetes Objekt (0x154e5d0b0)  (Entität: Liste; ID: 0xd0000000001c0000  ; data: items = ("0xd000000000540002) "," 0xd000000000580002 "); name =" List 6 ";) mit Objekten (  (Entität: Element; ID: 0xd000000000540002  ; Daten: ),  (Entität: Element; ID: 0xd000000000580002  ; Daten: ))

Wir sehen keinen Beziehungsfehler mehr, aber immer noch einen Fehler. Worum geht es? Core Data kann uns nur die Anzahl der Elemente für die Liste angeben, indem der Beziehungsfehler ausgelöst oder behoben wird. Dies bedeutet jedoch nicht, dass Core Data die Elemente der Beziehung auflöst. Die Ausgabe in der Konsole bestätigt dies.

Wir können sehen, dass die Datensätze für die Elemente vorhanden sind, einschließlich der Kennung, die Core Data intern verwendet. Das Datenwörterbuch wird jedoch als Fehler markiert. Core Data gibt uns nur das, wonach wir fragen. Glücklicherweise werden die wichtigsten Details von Core Data behandelt.

Lassen Sie uns etwas tiefer graben und eines der Elemente aus der Liste holen. Wir machen das, indem wir anrufen anyObject () auf der Artikel Objekt. Wir drucken den resultierenden Artikel in der fünften Druckaussage.

5:  (Entität: Element; ID: 0xd000000000540002  ; Daten: )

Die Ausgabe sollte Sie nicht überraschen. Die Ausgabe bestätigt, dass es sich um eine Artikel Entität. Kein Wunder, dass das Datenwörterbuch immer noch als Fehler gekennzeichnet ist. In der sechsten print-Anweisung drucken wir die Name Attribut des Artikels.

6: Punkt 0

Da wir nach dem Wert eines Attributs des Datensatzes fragen, löst Core Data den Fehler aus, um diesen Wert zu erhalten. Es ruft die Daten für das Element ab und füllt das Datenwörterbuch. Die siebte Druckerklärung bestätigt diese Feststellungen.

7  (Entität: Element; ID: 0xd000000000540002  ; data: list = "0xd0000000001c0000 "; name =" Item 0 ";)

Das Datenwörterbuch enthält die Name Attribut sowie die Liste Beziehung. Die achte und letzte print-Anweisung zeigt, dass der Beziehungsfehler der Liste Objekt ist aufgelöst.

8  (Entität: Liste; ID: 0xd0000000001c0000  ; data: items = ("0xd000000000540002) "," 0xd000000000580002 "); name =" List 6 ";)

Fehler kann nicht behoben werden

Ich habe mich entschlossen, diesen Artikel zu schreiben, um ein Problem zu erklären, mit dem viele Entwickler, die Core Data verwenden, an dem einen oder anderen Punkt auftreten und einen Fehler auslösen, der nicht behoben werden kann. Das Problem ist einfach. Angenommen, Sie haben eine Liste mit einer Reihe von Elementen, und der Benutzer löscht die Liste zu einem bestimmten Zeitpunkt. Was passiert mit den Elementen dieser Liste? Was passiert, wenn Core Data versucht, das System auszulösen? Liste Beziehung eines der Elemente, die zu dieser Liste gehörten? Lass es uns herausfinden.

Lassen Sie uns das von GitHub heruntergeladene Projekt noch einmal besuchen. Öffnen Fehler.xcdatamodeld, das Datenmodell des Projekts. Wähle aus Artikel Beziehung der Liste Entität und öffnen Sie die Datenmodellinspektor zur Rechten. Was uns interessiert, ist das Regel löschen, was ist momentan eingestellt auf Kaskade. Dies bedeutet, dass jedes Element der Liste gelöscht wird, wenn die Liste gelöscht wird. Dies ist sinnvoll, da wir nicht auf Elemente verzichten möchten, die keiner Liste zugeordnet sind.

Wähle aus Liste Beziehung der Artikel Entität und öffnen Sie die Datenmodellinspektor. Das Regel löschen dieser Beziehung ist auf gesetzt Aufheben. Dies bedeutet, dass das Ziel der Beziehung, das Listenobjekt, auf null gesetzt wird, wenn der Zieldatensatz (das Element) gelöscht wird. Dies ist die Standard-Löschregel für eine Beziehung.

Führen Sie die Anwendung aus, erstellen Sie einige Listen und Elemente und tippen Sie auf Artikel Schaltfläche oben links, um jedes Element anzuzeigen, das Sie erstellt haben. Zapfhahn Stornieren Löschen Sie oben links eine der Listen und tippen Sie auf Artikel erneut, um zu sehen, was sich geändert hat. Wie erwartet wurden die mit der gelöschten Liste verknüpften Elemente auch von Core Data gelöscht. Das ist keine Magie. Core Data führt einfach die Löschregeln aus, die wir im Datenmodell definiert haben.

Wir können daraus schließen, dass die Beziehungen für diese Art von Anwendung richtig eingerichtet sind. Was passiert aber, wenn wir die Löschregeln nicht korrekt konfigurieren? Öffnen Sie das Datenmodell und legen Sie die Löschregel für beide Beziehungen fest, Artikel und Liste, zu Keine Aktion. Starten Sie die Anwendung erneut und erstellen Sie einige Listen und Elemente. Wenn Sie eine Liste löschen, tippen Sie auf Artikel Wenn Sie oben links auf die Schaltfläche klicken, um alle Elemente im permanenten Speicher anzuzeigen, sollten die mit der gelöschten Liste verknüpften Elemente noch vorhanden sein. Sie wurden nicht gelöscht, obwohl die Liste, zu der sie gehören, entfernt wurde.

Tippen Sie auf eine der Listen und sehen Sie, was passiert. Wenn Sie die Anwendung unter iOS 8 ausführen, stürzt die Anwendung aufgrund einer nicht erfassten Ausnahme ab. Wenn Sie die Anwendung unter iOS 9 ausführen, wird nur ein Fehler in der Konsole angezeigt. Hör auf, deinen Kopf zu kratzen und lass uns das zusammen herausfinden.

Objekt nicht erreichbar

Obwohl die Anwendung unter iOS 8 abstürzt, ist die Ausgabe in der Konsole klar und auf den Punkt.

Fehler [7189: 2427906] *** Beenden der App aufgrund der nicht erfassten Ausnahme 'NSObjectInaccessibleException', Grund: 'CoreData konnte keinen Fehler für' 0x175b2ba0 beheben "

Die erste Bemerkung ist, dass die Anwendung aufgrund einer nicht erfassten Ausnahme abgestürzt ist. Wichtiger für unsere Diskussion ist jedoch der Grund, warum die Ausnahme ausgelöst wurde. Core Data sagt uns, dass ein Fehler für eine Beziehung nicht behoben werden konnte. Die Beziehung, auf die sich Core Data bezieht, ist die Liste Beziehung des Elements, das wir in der Tabellensicht angetippt haben.

Wenn Sie sich die Implementierung von ansehen tableView (tableView: didSelectRowAtIndexPath :), Dann werden Sie verstehen, warum Core Data eine Ausnahme auslöste. Bei dieser Methode rufen wir das Element ab, das der Benutzer angetippt hat, und drucken den Namen der Liste an die Konsole.

func tableView (tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) tableView.deselectRowAtIndexPath (indexPath, animiert: true), wenn item = self.fetchedResultsController.objectAtIndexPath (indexPath) als? Element print (item.list? .Name)

Weil der Liste Beziehung ist ein Fehler, Core Data muss den Fehler auslösen, um ihn zu beheben. Dies bedeutet, dass Core Data den persistenten Speicher nach dem Listendatensatz fragt. Das Problem ist, dass sich der Datensatz nicht mehr im permanenten Speicher befindet, weshalb eine Ausnahme ausgelöst wurde.

Nicht zugreifbare Fehler löschen

Bis iOS 9 war dies immer das Standardverhalten. Ab iOS 9 löst Core Data keine Ausnahme mehr aus. Stattdessen wird eine kryptische Nachricht an der Konsole protokolliert. Obwohl die Nachricht unklar ist, enthält sie immer noch den Grund des Problems.

Fehler [2806: 1306995] CoreData: Warnung: Ein Verhalten des NSManagedObjectContext-Delegaten überschreibt die Fehlerbehandlung, um das Objekt mit der ID '0xd000000000140000 unbemerkt zu löschen 'und ersetze nil / 0 für alle Eigenschaftswerte, anstatt zu werfen.

Der Kernpunkt ist, dass Core Data keine Ausnahme mehr auslöst, wenn ein Fehler nicht behoben werden kann. Die gute Nachricht ist, dass Ihre Anwendung nicht mehr abstürzt, wenn Sie die Ausnahme nicht wahrnehmen.

Der Grund für das Auslösen einer Ausnahme unter iOS 9 ist auf die Einführung einer neuen Eigenschaft zurückzuführen, shouldDeleteInaccessibleFaults, und eine neue Methode, shouldHandleInaccessibleFault (_: forObjectID: triggeredByProperty :), auf der NSManagedObjectContext Klasse. Ich werde diese Ergänzungen in diesem Tutorial nicht behandeln.

Was Sie beachten müssen, ist, dass iOS 9 standardmäßig die Einstellung setzt shouldDeleteInaccessibleFaults zu wahr. Dies bedeutet, dass ein nicht zugreifbares verwaltetes Objekt als gelöscht markiert und seine Eigenschaften auf Null gesetzt werden. Ob shouldDeleteInaccessibleFaults ist eingestellt auf falsch, Core Data wird durch Auslösen einer Ausnahme auf das alte Verhalten zurückgesetzt.

Natürlich die shouldDeleteInaccessibleFaults Eigentum geht Hand in Hand mit shouldHandleInaccessibleFault (_: forObjectID: triggeredByProperty :). Wenn Sie diese Methode überschreiben, können Sie mit unerreichbaren Objekten eleganter umgehen.

Umgang mit Fehlern

Wenn eine Ausnahme auftritt, weil Core Data einen Fehler nicht beheben kann, denken Sie möglicherweise, dass Core Data etwas aggressiv ist. Diese Reaktion ist für Menschen, die in diesem Rahmen neu sind, durchaus üblich. Die Wahrheit ist, dass der Entwickler schuld ist. Core Data wurde mit einem bestimmten Satz von Zielen entworfen und Fehler sind eine wesentliche Komponente, um diese Ziele zu erreichen.

Trotz ihres Namens sollten Fehler immer befriedigend sein. Wenn ein Fehler nicht erfüllt werden kann, bedeutet dies, dass das Datenmodell nicht ordnungsgemäß eingerichtet ist oder die Anwendung die vom Core Data-Framework festgelegten Regeln nicht einhält.

Im Core Data-Programmierhandbuch listet Apple eine Reihe von Szenarien auf, die zu Fehlern führen können, die nicht mehr erfüllt werden können. Die häufigsten Szenarien sind falsch konfigurierte Beziehungen (Löschregeln) und das Löschen eines verwalteten Objekts, während die Anwendung noch einen starken Verweis auf das verwaltete Objekt hat.

Seien Sie gewarnt, es gibt andere mögliche Szenarien. Aus meiner Erfahrung haben Entwickler oft Probleme mit dem Core Data Stack. Wenn die Anwendung beispielsweise einen starken Verweis auf ein verwaltetes Objekt enthält und den verwalteten Objektkontext dieses verwalteten Objekts irgendwann aufhebt, kann Core Data keine Fehler mehr für das verwaltete Objekt beheben. Ich hoffe, Sie verstehen, dass dies nicht passieren darf, wenn Sie nach den Regeln von Core Data spielen.

Fazit

Kerndatenfehler sind unglaublich nützlich und eine Schlüsselkomponente des Persistenz-Frameworks von Apple. Ich hoffe, dieser Artikel hat Ihnen gezeigt, wie Sie mit Fehlern umgehen und wo Sie suchen müssen, wenn Sie auf Fehler stoßen, die nicht erfüllt werden können. Wenn Sie Fragen haben, können Sie diese gerne in den folgenden Kommentaren hinterlassen oder mich auf Twitter kontaktieren.