Qu'est-ce qu'une erreur de données principale?

Les défauts sont une composante essentielle de Core Data. Même si le terme semble inquiétant, les erreurs sont inhérentes au cycle de vie d'un enregistrement Core Data. Dans ce didacticiel, vous apprendrez quelles sont les erreurs, comment les gérer et comment résoudre les problèmes liés à l'erreur..

Conditions préalables

Les données de base étant un sujet avancé, je suppose que vous connaissez déjà le développement Xcode et iOS. Bien que j'utilise le langage de programmation Swift pour ce didacticiel, tout ce didacticiel est également applicable à Objective-C..

Wat est une faute?

Core Data est très performant grâce au travail acharné de son équipe Core Data. Core Data est hautement optimisé pour réduire l'encombrement de la mémoire sans sacrifier les performances. La défaillance est l’une des techniques que Core Data utilise pour consommer le moins de mémoire possible..

La défaillance n'est pas propre à Core Data. Une technique similaire est utilisée dans de nombreux autres frameworks, tels que Ember et Ruby on Rails. L'idée est simple, ne chargez des données que lorsque cela est nécessaire. Pour remédier aux erreurs, Core Data fait un peu de magie en créant des sous-classes personnalisées au moment de la compilation, qui représentent les erreurs. Voyons comment cela fonctionne avec un exemple.

J'ai créé un exemple d'application simple à utiliser. Téléchargez le projet Xcode depuis GitHub et ouvrez-le dans Xcode. Le projet utilise Swift 2.1, ce qui signifie que vous avez besoin de Xcode 7.1 ou supérieur pour satisfaire le compilateur..

Le modèle de données contient deux entités, liste et Article. Une liste peut avoir zéro ou plusieurs éléments et un élément est toujours lié à une liste. C'est un exemple classique d'une relation un à plusieurs.

Exécutez l'application et remplissez le magasin persistant, une base de données SQLite, avec certaines données en créant quelques listes et éléments. Quittez l'application lorsque vous avez terminé.

Défauts de tir

Vous devriez maintenant avoir une application avec quelques données. Voyons comment les erreurs fonctionnent dans la pratique en ajoutant des instructions d'impression. Ouvrir ListsViewController.swift et chercher prepareForSegue (_: expéditeur :). Dans cette méthode, nous récupérons la liste que l'utilisateur a sélectionnée dans la vue tableau. Décommentez les déclarations d'impression dans prepareForSegue (_: expéditeur :) comme indiqué dans l'implémentation ci-dessous.

override func prepareForSegue (segue: UIStoryboardSegue, expéditeur: AnyObject?) if segue.identifier == SegueListViewController garde let indexPath = tableView.indexPathForSelectedRow sinon retour // Liste de lecture let liste = self.fetched Liste print ("1: \ (liste)") si items = list.items print ("2: \ (items)") print ("3: \ (items.count)") print ("4: \ (items) ") si item = items.anyObject () print (" 5: \ (item) ") print (" 6: \ (item.name) ") print (" 7: \ (item) ")  print ("8: \ (list)") // Récupérer le contrôleur de vue de destination laisser listViewController = segue.destinationViewController as! ListViewController // Configurer View Controller listViewController.list = list

Exécutez l'application et appuyez sur l'une des listes dans le contrôleur d'affichage des listes. L'exemple n'est intéressant que si vous appuyez sur une liste à laquelle un ou plusieurs éléments sont associés. Inspectons progressivement la sortie des instructions d'impression..

1:  (entité: liste; id: 0xd0000000001c0000  ; data: items = ""; name =" List 6 ";)

La première chose à noter est le nom de la classe, Faulting.List. C’est ce que nous attendons depuis que le module est nommé Faute et la classe est nommée liste. Dans Swift, le nom complet d'une classe est composé du nom du module et du nom de la classe..

La sortie dans la console affiche également le nom de l'entité, liste, et un dictionnaire de données. le prénom attribut est mis à Liste 6 tandis que le articles attribut est marqué comme faute relationnelle. C'est le premier type de défaut dans Core Data. Core Data comprend qu'il n'est pas nécessaire de charger la relation. Au lieu de cela, il marque la relation comme une faute. La deuxième déclaration imprimée le confirme, comme vous pouvez le voir ci-dessous..

2: Défaut des "éléments" de relation sur l'objet géré (0x154e5d0b0)  (entité: liste; id: 0xd0000000001c0000  ; data: items = ""; name =" List 6 ";)

La troisième déclaration écrite est toutefois plus intéressante. Il imprime le nombre d'éléments associés à la liste.

3: 2

Les données de base ne peuvent le faire que par le déclenchement de la faute relationnelle. Il demande au magasin persistant les éléments associés à la liste. Ceci, cependant, n'est qu'une partie de l'histoire, comme l'illustre la quatrième déclaration écrite.

4: relation 'éléments' sur objet géré (0x154e5d0b0)  (entité: liste; id: 0xd0000000001c0000  ; données: items = ("0xd000000000540002 "," 0xd000000000580002 "); name =" List 6 ";) avec des objets (  (entité: Item; id: 0xd000000000540002  ; Les données: ),  (entité: Item; id: 0xd000000000580002  ; Les données: ))

Nous ne voyons plus de faute relationnelle, mais nous voyons toujours une faute. De quoi s'agit-il? Les données de base ne peuvent nous donner le nombre d’éléments de la liste qu’en renvoyant ou en résolvant l’erreur relationnelle. Toutefois, cela ne signifie pas que Core Data résout les éléments de la relation. La sortie dans la console le confirme.

Nous pouvons voir que les enregistrements pour les éléments sont là, y compris l'identifiant utilisé en interne par Core Data. Le dictionnaire de données, cependant, est marqué comme une faute. Encore une fois, Core Data ne nous donne que ce que nous demandons. Heureusement, les détails essentiels sont traités par Core Data..

Approfondissons un peu et récupérons l'un des éléments de la liste. Nous faisons cela en appelant anyObject () sur le articles objet. Nous imprimons l'article résultant dans la cinquième déclaration d'impression.

5:  (entité: Item; id: 0xd000000000540002  ; Les données: )

La sortie ne devrait pas vous surprendre. La sortie confirme que nous avons affaire à un Article entité. Sans surprise, le dictionnaire de données est toujours marqué comme une faute. Dans la sixième déclaration d'impression, nous imprimons le prénom attribut de l'article.

6: élément 0

Parce que nous demandons la valeur d'un attribut de l'enregistrement, Core Data déclenche la faute pour nous donner cette valeur. Il récupère les données pour l'élément et remplit le dictionnaire de données. La septième déclaration écrite confirme ces conclusions.

7:  (entité: Item; id: 0xd000000000540002  ; data: list = "0xd0000000001c0000 "; name =" Item 0 ";)

Le dictionnaire de données contient les prénom attribuer ainsi que le liste relation. La huitième et dernière déclaration imprimée montre que la faille de la relation de liste l'objet est résolu.

8:  (entité: liste; id: 0xd0000000001c0000  ; données: items = ("0xd000000000540002 "," 0xd000000000580002 "); name =" List 6 ";)

Incapable de réparer une faute

J'ai décidé d'écrire cet article pour expliquer un problème rencontré par de nombreux développeurs utilisant Core Data à un moment ou à un autre, provoquant une erreur irrécupérable. Le problème est simple. Supposons que vous avez une liste avec un certain nombre d'éléments et que l'utilisateur supprime la liste à un moment donné. Qu'advient-il des éléments de cette liste? Que se passe-t-il si Core Data tente de déclencher le liste relation de l'un des éléments qui ont appartenu à cette liste? Découvrons-le.

Revenons sur le projet que vous avez téléchargé depuis GitHub. Ouvrir Faulting.xcdatamodeld, le modèle de données du projet. Sélectionnez le articles relation de la liste entité et ouvrez le Inspecteur de modèle de données sur la droite. Ce qui nous intéresse, c’est le Supprimer la règle, qui est actuellement réglé sur Cascade. Cela signifie que chaque élément de la liste est supprimé lorsque la liste est supprimée. Cela a du sens puisque nous ne voulons pas que des éléments abandonnés ne soient associés à une liste.

Sélectionnez le liste relation de la Article entité et ouvrez le Inspecteur de modèle de données. le Supprimer la règle de cette relation est définie sur Annuler. Cela signifie que la destination de la relation, l'objet liste, est définie sur null lorsque l'enregistrement de destination, l'élément, est supprimé. Ceci est la règle de suppression par défaut pour une relation.

Exécutez l'application, créez quelques listes et éléments, puis appuyez sur le bouton Articles bouton en haut à gauche pour afficher chaque élément que vous avez créé. Robinet Annuler en haut à gauche, supprimez une des listes et appuyez sur le bouton Articles Cliquez à nouveau sur le bouton pour voir ce qui a changé. Comme prévu, les éléments associés à la liste supprimée ont également été supprimés par Core Data. Ce n'est pas magique. Core Data exécute simplement les règles de suppression définies dans le modèle de données..

Nous pouvons en conclure que les relations sont correctement définies pour ce type d’application. Mais que se passe-t-il si nous ne configurons pas correctement les règles de suppression? Ouvrez le modèle de données et définissez la règle de suppression des deux relations., articles et liste, à Pas d'action. Relancez l’application et créez quelques listes et éléments. Si vous supprimez une liste et touchez le Articles bouton en haut à gauche pour voir tous les éléments du magasin persistant, les éléments associés à la liste supprimée doivent toujours être là. Ils n'ont pas été supprimés même si la liste à laquelle ils appartiennent a été supprimée.

Appuyez sur l'une des listes et voyez ce qui se passe. Si vous exécutez l'application sur iOS 8, l'application se bloquera en raison d'une exception non interceptée. Si vous exécutez l'application sur iOS 9, vous ne verrez qu'une erreur dans la console. Arrêtez de vous gratter la tête et découvrons cela ensemble.

Objet inaccessible

Même si l'application se bloque sur iOS 8, le résultat affiché dans la console est clair et précis..

Erreur [7189: 2427906] *** Arrêt de l'application en raison d'une exception non capturée 'NSObjectInaccessibleException', raison: 'CoreData n'a pas pu corriger l'erreur' 0x175b2ba0 "

La première chose à noter est que l'application s'est bloquée en raison d'une exception non interceptée. Ce qui est plus important pour notre discussion, cependant, est la raison pour laquelle l'exception a été levée. Core Data nous dit qu’elle n’était pas en mesure de réparer une faute dans une relation. La relation entre les données de base est la liste relation de l'élément que nous avons exploité dans la vue de table.

Si vous regardez la mise en œuvre de tableView (tableView: didSelectRowAtIndexPath :), alors vous comprendrez pourquoi Core Data a lancé une exception. Dans cette méthode, nous récupérons l'élément que l'utilisateur a tapé et nous imprimons le nom de la liste sur la console..

func tableView (tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) tableView. Item print (item.list? .Name)

Parce que le liste relation est une faute, Core Data doit la déclencher pour la résoudre. Cela signifie que Core Data demande au magasin persistant l'enregistrement de la liste. Le problème est que l'enregistrement n'est plus dans le magasin persistant, c'est pourquoi une exception a été levée..

Supprimer les défauts inaccessibles

Jusqu'à iOS 9, cela a toujours été le comportement par défaut. Depuis iOS 9, Core Data ne lance plus une exception. Au lieu de cela, il enregistre un message crypté sur la console. Bien que le message ne soit pas clair, il contient toujours la raison du problème.

Faulting [2806: 1306995] CoreData: avertissement: un délégué NSManagedObjectContext a ignoré le comportement de traitement des erreurs pour supprimer en silence l'objet portant l'ID '0xd000000000140000. 'et substitue nil / 0 pour toutes les valeurs de propriété au lieu de lancer.

L'essentiel est que Core Data ne lève plus d'exception lorsqu'il est incapable de corriger une erreur. La bonne nouvelle est que votre application ne plante plus si vous ne détectez pas l'exception..

La raison pour laquelle une exception n'a pas été lancée sur iOS 9 est due à l'introduction d'une nouvelle propriété., devraitDeleteInaccessibleFaults, et une nouvelle méthode, shouldHandleInaccessibleFault (_: forObjectID: triggeredByProperty :), sur le NSManagedObjectContext classe. Je ne couvrirai pas ces ajouts dans ce tutoriel.

Ce que vous devez vous rappeler est que iOS 9, par défaut, définit le devraitDeleteInaccessibleFaults à vrai. Cela signifie qu'un objet géré inaccessible est marqué comme supprimé et que ses propriétés sont mises à zéro. Si devraitDeleteInaccessibleFaults est réglé sur faux, Core Data rétablit l'ancien comportement en lançant une exception.

Bien sûr, le devraitDeleteInaccessibleFaults la propriété va de pair avec shouldHandleInaccessibleFault (_: forObjectID: triggeredByProperty :). Si vous substituez cette méthode, vous pouvez gérer les objets inaccessibles de manière plus élégante..

Gestion des défauts

Lorsque vous rencontrez une exception due à l'impossibilité pour Core Data de remédier à une erreur, vous pouvez penser que Core Data est un peu agressif. Cette réaction est assez courante chez les nouveaux venus dans le framework. La vérité est que le développeur est en faute. Les données de base ont été conçues avec un ensemble d'objectifs spécifiques à l'esprit et les erreurs sont un élément essentiel pour atteindre ces objectifs..

Malgré leur nom, les fautes doivent toujours être réalisables. Si une erreur ne peut pas être remplie, cela signifie que le modèle de données n'est pas configuré correctement ou que l'application ne respecte pas les règles définies par le cadre Core Data..

Dans le Guide de programmation de base de données, Apple répertorie un certain nombre de scénarios pouvant conduire à des erreurs impossibles à résoudre. Les scénarios les plus courants sont des relations mal configurées (règles de suppression) et la suppression d'un objet géré alors que l'application possède toujours une référence forte à cet objet géré..

Soyez averti, il existe d'autres scénarios possibles. D'après mon expérience, les développeurs rencontrent souvent des problèmes similaires en raison d'un problème lié à la pile de données centrale. Par exemple, si l'application conserve une référence forte à un objet géré et, à un moment donné, libère le contexte d'objet géré de cet objet géré, Core Data n'est plus en mesure de remédier aux erreurs de cet objet géré. J'espère que vous comprenez que cela ne devrait pas arriver si vous respectez les règles de Core Data.

Conclusion

Les défauts de données de base sont incroyablement utiles et constituent un élément clé du cadre de persistance d’Apple. J'espère que cet article vous a appris comment traiter les erreurs et où chercher si vous rencontrez des erreurs impossibles à combler. Si vous avez des questions, n'hésitez pas à les laisser dans les commentaires ci-dessous ou à me contacter sur Twitter.