Wat is een kerngegevensfout?

Fouten zijn een essentieel onderdeel van kerngegevens. Hoewel de term onheilspellend klinkt, zijn fouten inherent aan de levenscyclus van een Core Data-record. In deze zelfstudie leert u wat fouten zijn, hoe u hiermee om moet gaan en hoe u problemen met foutmeldingen kunt opsporen.

voorwaarden

Core Data is een geavanceerd onderwerp, dus ik ga ervan uit dat je al bekend bent met de ontwikkeling van Xcode en iOS. Hoewel ik de Swift-programmeertaal voor deze zelfstudie gebruik, is alles in deze zelfstudie ook van toepassing op Objective-C.

Wat is een fout?

Core Data is erg goed in wat het doet dankzij het harde werk van het Core Data-team van Apple. Core Data is sterk geoptimaliseerd om zijn geheugenvoetafdruk laag te houden zonder in te boeten op de prestaties. Faulting is een van de technieken die Core Data gebruikt om zo min mogelijk geheugen te verbruiken.

Fouten is niet uniek voor Core Data. Een vergelijkbare techniek wordt in veel andere frameworks gebruikt, zoals Ember en Ruby on Rails. Het idee is eenvoudig, laad alleen gegevens wanneer dat nodig is. Om fouten te maken, doet Core Data een beetje magie onder de motorkap door tijdens de compilatie aangepaste subklassen te maken die de fouten representeren. Laten we eens kijken hoe dit werkt met een voorbeeld.

Ik heb een eenvoudige voorbeeldtoepassing gemaakt om mee te werken. Download het Xcode-project vanuit GitHub en open het in Xcode. Het project maakt gebruik van Swift 2.1, wat betekent dat je Xcode 7.1 of hoger nodig hebt om aan de compiler te voldoen.

Het gegevensmodel bevat twee entiteiten, Lijst en Item. Een lijst kan nul of meer items bevatten en een item is altijd gekoppeld aan een lijst. Het is een klassiek voorbeeld van een een-op-veel-relatie.

Voer de toepassing uit en vul het persistente archief, een SQLite-database, met enkele gegevens door een paar lijsten en items te maken. Verlaat de applicatie als u klaar bent.

Fires Fults

Je zou nu een applicatie met wat gegevens moeten hebben. Laten we eens kijken hoe fouten in de praktijk werken door enkele afdrukinstructies toe te voegen. Open ListsViewController.swift en zoek naar prepareForSegue (_: afzender :). In deze methode halen we de lijst op die de gebruiker in de tabelweergave heeft geselecteerd. Maak een commentaar op de afdrukinstructies in prepareForSegue (_: afzender :) zoals getoond in de onderstaande implementatie.

override func prepareForSegue (segue: UIStoryboardSegue, sender: AnyObject?) if segue.identifier == SegueListViewController guard let indexPath = tableView.indexPathForSelectedRow else return // Lijst met ophaallijsten = self.fetchedResultsController.objectAtIndexPath (indexPath) as! Lijst afdrukken ("1: \ (lijst)") als let items = list.items print ("2: \ (items)") print ("3: \ (items.count)") print ("4: \ (items) ") als let item = items.anyObject () print (" 5: \ (item) ") print (" 6: \ (item.name) ") print (" 7: \ (item) ")  print ("8: \ (lijst)") // Bestemmingsoverzicht ophalen Controlelijst weergevenViewController = segue.destinationViewController as! ListViewController // Configureer View Controller-lijstViewController.list = lijst

Start de applicatie en tik op een van de lijsten in de lijstcontroller. Het voorbeeld is alleen interessant als u op een lijst tikt waaraan een of meer items zijn gekoppeld. Laten we de uitvoer van de printinstructies stap voor stap inspecteren.

1:  (entiteit: lijst; id: 0xd0000000001c0000  ; gegevens: items = ""; name =" Lijst 6 ";)

Het eerste dat opvalt is de klassenaam, Faulting.List. Dit is wat we verwachten sinds de naam van de module vastgelopen en de klas is genoemd Lijst. In Swift bestaat de volledige klassenaam van een klas uit de naam van de module en de naam van de klas.

De uitvoer in de console toont ook de naam van de entiteit, Lijst, en een woordenboek met gegevens. De naam attribuut is ingesteld op Lijst 6 Terwijl de items attribuut is gemarkeerd als a relatie fout. Dit is het eerste type fout in Core Data. Core Data begrijpt dat het niet nodig is om de relatie te laden. In plaats daarvan markeert het de relatie als een fout. De tweede printinstructie bevestigt dit zoals u hieronder kunt zien.

2: Fout relatie 'items' op beheerd object (0x154e5d0b0)  (entiteit: lijst; id: 0xd0000000001c0000  ; gegevens: items = ""; name =" Lijst 6 ";)

De derde printzin is echter interessanter. Het drukt het aantal items af dat aan de lijst is gekoppeld.

3: 2

Kerngegevens kunnen dit alleen doen door de relatiefout te activeren. Het vraagt ​​de persistente winkel voor de items die aan de lijst zijn gekoppeld. Dit is echter slechts een deel van het verhaal zoals geïllustreerd door de vierde printverklaring.

4: Relatie 'items' op beheerd object (0x154e5d0b0)  (entiteit: lijst; id: 0xd0000000001c0000  ; gegevens: items = ("0xd000000000540002 "," 0xd000000000580002 "); name =" Lijst 6 ";) met objecten (  (entiteit: item; id: 0xd000000000540002  ; gegevens: ),  (entiteit: item; id: 0xd000000000580002  ; gegevens: ))

We zien niet langer een relatiefout, maar we zien nog steeds een fout. Waar gaat dat over? Kerngegevens kunnen ons alleen het aantal items voor de lijst geven door de relatiefout te activeren of te verhelpen. Dit betekent echter niet dat Core Data de items van de relatie oplost. De uitvoer in de console bevestigt dit.

We kunnen zien dat de records voor de items daar zijn, inclusief de identifier die Core Data intern gebruikt. Het gegevenswoordenboek is echter gemarkeerd als een fout. Nogmaals, Core Data geeft ons alleen wat we vragen. Gelukkig worden de gedetailleerde gegevens verwerkt door Core Data.

Laten we een beetje dieper graven en een van de items uit de lijst ophalen. We doen dit door te bellen anyObject () op de items voorwerp. We printen het resulterende item in de vijfde printopgave.

5:  (entiteit: item; id: 0xd000000000540002  ; gegevens: )

De output mag je niet verbazen. De uitvoer bevestigt dat we te maken hebben met een Item entiteit. Zoals te verwachten, wordt het data woordenboek nog steeds als een fout gemarkeerd. In de zesde printzin drukken we de naam kenmerk van het item.

6: Item 0

Omdat we om de waarde van een attribuut van de record vragen, vuurt Core Data de fout aan om ons die waarde te geven. Het haalt de gegevens voor het item op en vult het gegevenswoordenboek. De zevende printverklaring bevestigt deze bevindingen.

7:  (entiteit: item; id: 0xd000000000540002  ; gegevens: list = "0xd0000000001c0000 "; name =" Item 0 ";)

Het gegevenswoordenboek bevat de naam attribuut zo goed als de lijst relatie. De achtste en laatste printinstructie laat zien dat de relatie fout van de lijst object is opgelost.

8:  (entiteit: lijst; id: 0xd0000000001c0000  ; gegevens: items = ("0xd000000000540002 "," 0xd000000000580002 "); name =" Lijst 6 ";)

Kan een fout niet vervullen

Ik besloot dit artikel te schrijven om een ​​probleem uit te leggen waar veel ontwikkelaars met Core Data op de een of andere manier tegen aan lopen, een fout aanmakend die niet kan worden vervuld. Het probleem is eenvoudig. Laten we aannemen dat je een lijst met een aantal items hebt en dat de gebruiker op een gegeven moment de lijst verwijdert. Wat gebeurt er met de items van die lijst? Wat gebeurt er als Core Data probeert het lijst relatie van een van de items die tot die lijst behoorden? Laten wij het uitzoeken.

Laten we het project opnieuw bekijken dat je hebt gedownload vanuit GitHub. Open Faulting.xcdatamodeld, het datamodel van het project. Selecteer de items relatie van de Lijst entiteit en open de Gegevensmodelinspecteur aan de rechterkant. Wat voor ons van belang is, is het Regel verwijderen, die momenteel is ingesteld op Cascade. Dit betekent dat elk item van de lijst wordt verwijderd wanneer de lijst wordt verwijderd. Dit is logisch, want we willen geen items verlaten die niet aan een lijst zijn gekoppeld.

Selecteer de lijst relatie van de Item entiteit en open de Gegevensmodelinspecteur. De Regel verwijderen van deze relatie is ingesteld op vernietigen. Dit betekent dat de bestemming van de relatie, het lijstobject, is ingesteld op nul wanneer de bestemmingsrecord, het item, wordt verwijderd. Dit is de standaard verwijderingsregel voor een relatie.

Voer de applicatie uit, maak een paar lijsten en items en tik op items knop links bovenaan om elk item weer te geven dat je hebt gemaakt. Tik annuleren linksboven verwijdert u een van de lijsten en tikt u op items opnieuw indrukken om te zien wat er is gewijzigd. Zoals verwacht, zijn de items die zijn gekoppeld aan de verwijderde lijst ook verwijderd door Core Data. Dit is geen magie. Core Data voert eenvoudig de verwijderingsregels uit die we in het datamodel hebben gedefinieerd.

We kunnen concluderen dat de relaties correct zijn ingesteld voor dit type applicatie. Maar wat gebeurt er als we de verwijderingsregels niet correct configureren? Open het gegevensmodel en stel de verwijderingsregel van beide relaties in, items en lijst, naar Geen actie. Start de applicatie opnieuw en maak een paar lijsten en items. Als u een lijst verwijdert en op de items knop in de linker bovenhoek om elk item in het permanente winkel te zien, de items behorende bij de verwijderde lijst moeten er nog steeds zijn. Ze zijn niet verwijderd, ook al is de lijst waartoe ze behoren, verwijderd.

Tik op een van de lijsten en kijk wat er gebeurt. Als u de toepassing op iOS 8 gebruikt, loopt de toepassing vast vanwege een niet-afgevangen uitzondering. Als u de toepassing op iOS 9 uitvoert, ziet u alleen een fout in de console. Stop met krabben aan je hoofd en laten we samen dit uitzoeken.

Object ontoegankelijk

Hoewel de applicatie crasht op iOS 8, is de uitvoer die we in de console zien duidelijk en to the point.

Faulting [7189: 2427906] *** Beëindigen app vanwege niet-afgevangen uitzondering 'NSObjectInaccessibleException', reden: 'CoreData kon een fout niet invullen voor' 0x175b2ba0 "

Het eerste dat opvalt, is dat de toepassing is gecrasht vanwege een niet-afgevangen uitzondering. Wat echter belangrijker is voor onze discussie, is de reden waarom de uitzondering werd gegooid. Core Data vertelt ons dat het niet in staat was om een ​​fout voor een relatie te vervullen. De relatie waarnaar Core Data verwijst, is de lijst relatie van het item dat we in de tabelweergave hebben gebruikt.

Als je kijkt naar de implementatie van tableView (TableView: didSelectRowAtIndexPath :), dan zul je begrijpen waarom Core Data een uitzondering heeft geworpen. In deze methode halen we het item op dat de gebruiker heeft aangeraakt en drukt de naam van de lijst af naar de console.

func tableView (tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) tableView.deselectRowAtIndexPath (indexPath, geanimeerd: true) indien item = self.fetchedResultsController.objectAtIndexPath (indexPath) als? Artikel print (item.list? .Name)

Omdat het lijst relatie is een fout, Core Data moet de fout oplossen om het op te lossen. Dit betekent dat Core Data de persistent store om het lijstrecord vraagt. Het probleem is dat het record niet langer in de permanente winkel staat. Daarom is er een uitzondering gegenereerd.

Verwijder ontoegankelijke fouten

Tot iOS 9 was dit altijd het standaardgedrag. Vanaf iOS 9 werpt Core Data niet langer een uitzondering. In plaats daarvan logt het een cryptisch bericht in de console. Ondanks dat de boodschap onduidelijk is, bevat deze nog steeds de reden van het probleem.

Faulting [2806: 1306995] CoreData: waarschuwing: een NSManagedObjectContext-deelnemer heeft het probleem met foutafhandeling overtroffen om het object stil te verwijderen met ID '0xd000000000140000 en vervang nul / 0 voor alle eigenschapswaarden in plaats van gooien.

De kern is dat Core Data niet langer een uitzondering werpt als het niet in staat is om een ​​fout te vervullen. Het enigszins goede nieuws is dat je applicatie niet meer crasht als je de uitzondering niet oploopt.

De reden voor het niet gooien van een uitzondering op iOS 9 is te wijten aan de introductie van een nieuwe eigenschap, shouldDeleteInaccessibleFaults, en een nieuwe methode, shouldHandleInaccessibleFault (_: forObjectID: triggeredByProperty :), op de NSManagedObjectContext klasse. Ik zal deze toevoegingen niet behandelen in deze zelfstudie.

Wat u moet onthouden, is dat iOS 9 standaard het shouldDeleteInaccessibleFaults naar waar. Dit betekent dat een ontoegankelijk beheerd object is gemarkeerd als verwijderd en dat de eigenschappen ervan op nul worden gezet. Als shouldDeleteInaccessibleFaults ingesteld op vals, Kerngegevens keren terug naar het oude gedrag door een uitzondering te geven.

Natuurlijk, de shouldDeleteInaccessibleFaults eigendom gaat hand in hand met shouldHandleInaccessibleFault (_: forObjectID: triggeredByProperty :). Als u deze methode overschrijft, kunt u ontoegankelijke objecten eleganter afhandelen.

Fouten afhandelen

Wanneer u een uitzondering tegenkomt omdat Core Data niet in staat is om een ​​fout te vervullen, denkt u misschien dat Core Data een beetje agressief is. Deze reactie is vrij gebruikelijk voor mensen die nieuw zijn in het kader. De waarheid is dat de ontwikkelaar een fout heeft begaan. Core Data is ontworpen met een specifieke set doelen in het achterhoofd en faulting is een essentieel onderdeel om deze doelen te realiseren.

Ondanks hun naam moeten fouten altijd vervuld zijn. Als niet aan een fout kan worden voldaan, betekent dit dat het datamodel niet correct is ingesteld of dat de toepassing niet voldoet aan de regels die zijn uiteengezet in het Core Data-raamwerk..

In de Core Data Programming Guide somt Apple een aantal scenario's op die kunnen leiden tot fouten die niet langer kunnen worden vervuld. De meest voorkomende scenario's zijn onjuist geconfigureerde relaties (verwijderingsregels) en het verwijderen van een beheerd object terwijl de toepassing nog steeds een sterke verwijzing naar dat beheerde object bevat.

Wees gewaarschuwd, er zijn andere mogelijke scenario's. Uit mijn ervaring komen ontwikkelaars vaak soortgelijke problemen tegen als gevolg van een probleem met de Core Data-stack. Als de toepassing bijvoorbeeld sterk refereert naar een beheerd object en op een bepaald punt de context van het beheerde object van dat beheerde object vrijgeeft, kan Core Data niet langer eventuele fouten voor dat beheerde object uitvoeren. Ik hoop dat je begrijpt dat dit niet zou moeten gebeuren als je volgens de regels van Core Data speelt.

Conclusie

Core Data-fouten zijn ongelooflijk nuttig en een belangrijk onderdeel van het persistentiekader van Apple. Ik hoop dat dit artikel je heeft geleerd hoe je met fouten moet omgaan en waar je op moet letten als je fouten tegenkomt die niet kunnen worden vervuld. Als u vragen heeft, kunt u deze achterlaten in de onderstaande opmerkingen of contact met mij opnemen via Twitter.