Vad är en kärnfel?

Fel är en väsentlig del av kärndata. Även om termen låter olycklig, är fel inneboende i livscykeln för en Core Data-post. I den här handledningen lär du dig vilka fel som finns, hur man hanterar dem och hur man debugger problem relaterade till fel.

förutsättningar

Core Data är ett avancerat ämne så jag antar att du redan är bekant med Xcode och iOS utveckling. Trots att jag använder Swift-programmeringsspråket för denna handledning, är allt i denna handledning också tillämpligt på Objective-C.

Wat är en felaktighet?

Core Data är mycket bra på vad det gör tack vare det hårda arbetet hos Apples Core Data-team. Core Data är mycket optimerad för att hålla sitt minnesfotavtryck lågt utan att offra prestanda. Fel är en av de metoder Core Data använder för att konsumera så lite minne som möjligt.

Fel är inte unik för Core Data. En liknande teknik används i många andra ramar, som Ember och Ruby on Rails. Idén är enkel, bara ladda data när det behövs. För att göra felaktigt arbete gör Core Data lite magi under huven genom att skapa egna underklasser vid kompileringstid som representerar felen. Låt oss se hur det här fungerar med ett exempel.

Jag har skapat ett enkelt provprogram för att arbeta med. Ladda ner Xcode-projektet från GitHub och öppna det i Xcode. Projektet använder Swift 2.1, vilket innebär att du behöver Xcode 7.1 eller högre för att uppfylla kompilatorn.

Datamodellen innehåller två enheter, Lista och Artikel. En lista kan ha noll eller flera objekt och ett objekt kopplas alltid till en lista. Det är ett klassiskt exempel på ett till många förhållande.

Kör programmet och fyll i den ihållande butiken, en SQLite-databas, med vissa data genom att skapa några listor och objekt. Avsluta ansökan när du är klar.

Firing Fel

Du borde nu ha en ansökan med viss data. Låt oss se hur fel fungerar i praktiken genom att lägga till några utskriftsdeklarationer. Öppna ListsViewController.swift och leta efter prepareForSegue (_: avsändare :). I den här metoden hämtar vi listan som användaren har valt i tabellvyn. Ovanstående utskriftsdeklarationerna i prepareForSegue (_: avsändare :) som visas i genomförandet nedan.

åsidosätta func prepareForSegue (segue: UIStoryboardSegue, avsändare: AnyObject?) om segue.identifier == SegueListViewController guard let indexPath = tableView.indexPathForSelectedRow else return // Hämta lista let list = self.fetchedResultsController.objectAtIndexPath (indexPath) som! Skriv ut ("1: \ (lista)") om objekten = list.items print ("2: \ (items)") skrivs ut ("3: \ (items.count)") ("6: \ (item.name)") skriv ut ("7: \ (item)")  skriv ut ("8: \ (lista)") // Hämta Destination View Controller låt listViewController = segue.destinationViewController as! ListViewController // Konfigurera View Controller listViewController.list = list

Kör programmet och tryck på en av listorna i listrutan. Exemplet är bara intressant om du knackar på en lista som har ett eller flera objekt associerade med det. Låt oss inspektera utskriften av utskriftsuppsättningarna steg för steg.

1:  (enhet: Lista; id: 0xd0000000001c0000  ; data: items = ""; name =" List 6 ";)

Det första att notera är klassnamnet, Faulting.List. Det här är vad vi förväntar oss sedan modulen heter faulting och klassen heter Lista. I Swift består klassens fullständiga klassnamn av modulens namn och klassens namn.

Utgången i konsolen visar också enhetens namn, Lista, och en ordbok av data. De namn attributet är inställt på Lista 6 medan objekt attributet är markerat som en förhållande fel. Det här är den första typen av fel i Core Data. Kärndata förstår att det inte finns något behov av att ladda förhållandet. Istället markerar det förhållandet som ett fel. Det andra utskriftsutrymmet bekräftar detta som du kan se nedan.

2: Förhållande "objekt" fel på hanterat objekt (0x154e5d0b0)  (enhet: Lista; id: 0xd0000000001c0000  ; data: items = ""; name =" List 6 ";)

Det tredje utskriftsmeddelandet är dock mer intressant. Den skriver ut antalet objekt som är associerade med listan.

3: 2

Core Data kan bara göra detta genom att avbryta relationen fel. Den frågar den ihållande affären för de objekt som är förknippade med listan. Detta är dock bara en del av berättelsen, som illustreras av den fjärde utgåvan.

4: Förhållande "objekt" på hanterat objekt (0x154e5d0b0)  (enhet: Lista; id: 0xd0000000001c0000  ; data: items = ("0xd000000000540002 "," 0xd000000000580002 "); name =" List 6 ";) med objekt (  (enhet: Artikel; id: 0xd000000000540002  ; data: ),  (enhet: Artikel, id: 0xd000000000580002  ; data: ))

Vi ser inte längre ett relationsfel, men vi ser fortfarande ett fel. Vad handlar det om? Kärndata kan bara ge oss antalet objekt för listan genom att skjuta eller lösa förhållandepåståendet. Detta innebär emellertid inte att Core Data löser objekten i relationen. Utgången i konsolen bekräftar detta.

Vi kan se att posterna för objekten finns där, inklusive identifieraren Core Data använder internt. Datalogiken är dock markerad som ett fel. Återigen ger Core Data oss bara det vi ber om. Lyckligtvis hanteras de nitty gritty detaljerna av Core Data.

Låt oss gräva lite djupare och hämta ett av föremålen från listan. Vi gör detta genom att ringa anyObject ()objekt objekt. Vi skriver ut det resulterande objektet i femte utskriftsutgåvan.

5:  (enhet: Artikel; id: 0xd000000000540002  ; data: )

Produktionen ska inte överraska dig. Utgången bekräftar att vi har att göra med en Artikel entitet. Inte förvånansvärt är dataordlistan fortfarande markerad som ett fel. I den sjätte utgåvan skriver vi ut namn attributet för objektet.

6: Föremål 0

Eftersom vi ber om värdet av ett attribut i posten, brinner Core Data felet för att ge oss det värdet. Det hämtar uppgifterna för objektet och fyller i dataordlistan. Den sjunde tryckningen bekräftar dessa resultat.

7:  (enhet: Artikel; id: 0xd000000000540002  ; data: list = "0xd0000000001c0000 "; name =" Item 0 ";)

Datahandboken innehåller namn attribut såväl som lista relation. Det åttonde och sista utskriftsutrymmet visar att förhållandet fel hos lista objektet är löst.

8:  (enhet: Lista; id: 0xd0000000001c0000  ; data: items = ("0xd000000000540002 "," 0xd000000000580002 "); name =" List 6 ";)

Kan inte uppfylla en felaktighet

Jag bestämde mig för att skriva den här artikeln för att förklara ett problem som många utvecklare som använder Core Data stöter på vid ett eller annat tillfälle, skjuter ett fel som inte kan uppfyllas. Problemet är enkelt. Låt oss anta att du har en lista med ett antal objekt och vid något tillfälle raderar användaren listan. Vad händer med objekten i den listan? Vad händer om Core Data försöker avfyra lista förhållande mellan ett av de föremål som tillhörde listan? Låt oss ta reda på.

Låt oss återfå projektet du har laddat ner från GitHub. Öppna Faulting.xcdatamodeld, projektets datamodell. Välj objekt förhållandet mellan Lista enhet och öppna Data Model Inspector till höger. Det som är av intresse för oss är Radera regeln, som för närvarande är inställd på Kaskad. Det betyder att varje lista i listan raderas när listan raderas. Det här är vettigt eftersom vi inte vill ha övergivna objekt som inte är kopplade till en lista.

Välj lista förhållandet mellan Artikel enhet och öppna Data Model Inspector. De Radera regeln av detta förhållande är inställt på Annullera. Det betyder att destinationsförhållandet, listobjektet, är inställt på null när destinationslistan, objektet, raderas. Detta är standard raderingsregeln för ett förhållande.

Kör programmet, skapa några listor och objekt och tryck på artiklar knappen längst upp till vänster för att visa varje objekt du har skapat. Kran Annullera längst upp till vänster raderar du en av listorna och trycker på artiklar knappen igen för att se vad som ändrats. Som förväntat har de objekt som hör samman med den raderade listan också raderats av Core Data. Det här är ingen magi. Core Data utförs enkelt de raderingsregler som vi definierade i datamodellen.

Vi kan dra slutsatsen att relationerna är korrekt inställda för denna typ av applikation. Men vad händer om vi inte konfigurerar raderingsreglerna korrekt. Öppna datamodellen och sätt raderingsraden för båda relationerna, objekt och lista, till Ingen action. Starta programmet igen och skapa några listor och objekt. Om du tar bort en lista och knackar på artiklar knappen längst upp till vänster för att se varje objekt i den ihållande butiken, bör objekten som hör till den raderade listan fortfarande finnas där. De togs inte bort även om listan till vilken de hör hemma har tagits bort.

Tryck på en av listorna och se vad som händer. Om du kör programmet iOS 8 kommer programmet att krascha på grund av ett oavsett undantag. Om du kör programmet iOS 9 ser du bara ett fel i konsolen. Sluta skrapa huvudet och låt oss räkna ut det här tillsammans.

Objektet otillgängligt

Även om programmet kraschar på iOS 8, är den produktion vi ser i konsolen klar och till den punkten.

Felaktigt [7189: 2427906] *** Avslutande app på grund av oavsett undantag "NSObjectInaccessibleException", anledningen: 'CoreData kunde inte uppfylla ett fel för' 0x175b2ba0 "

Det första att märka är att ansökan kraschade på grund av ett okänt undantag. Det som är viktigare för vår diskussion är emellertid anledningen till att undantaget kastades. Core Data berättar för oss att det inte kunde uppfylla ett fel för ett förhållande. Relationen Core Data hänvisar till är lista förhållandet mellan objektet vi knackade på i tabellvyn.

Om du tittar på genomförandet av Tableview (Tableview: didSelectRowAtIndexPath :), då förstår du varför Core Data kastade ett undantag. I den här metoden hämtar vi objektet som användaren knackade på och skriver ut namnet på listan till konsolen.

func tableView (tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) tableView.deselectRowAtIndexPath (indexPath, animated: true) om låt objekt = self.fetchedResultsController.objectAtIndexPath (indexPath) som? Föremål skriv ut (item.list? .Name)

Eftersom det lista förhållandet är ett fel, Core Data måste avhjälpa felet för att lösa det. Det betyder att Core Data frågar den vidhållna butiken för listposten. Problemet är att posten inte längre finns i den ihållande butiken, varför ett undantag kastades.

Ta bort otillgängliga fel

Fram till iOS 9 har detta alltid varit standardbeteendet. Som iOS 9 kastar Core Data inte längre ett undantag. Istället loggar det ett kryptiskt meddelande till konsolen. Trots att meddelandet är oklart innehåller det fortfarande orsaken till problemet.

Felaktigt [2806: 1306995] CoreData: varning: En NSManagedObjectContext-delegat överstyrde felhanteringsbeteendet för att tyst radera objektet med ID '0xd000000000140000 'och ersätt noll / 0 för alla egenskapsvärden istället för att kasta.

Kärnan är att Core Data inte längre kastar ett undantag när det inte kan uppfylla ett fel. Den något goda nyheten är att din ansökan inte längre kraschar om du inte fångar undantaget.

Anledningen till att inte slänga ett undantag på iOS 9 beror på införandet av en ny egendom, shouldDeleteInaccessibleFaults, och en ny metod, shouldHandleInaccessibleFault (_: forObjectID: triggeredByProperty :), på NSManagedObjectContext klass. Jag kommer inte att täcka dessa tillägg i denna handledning.

Vad du behöver komma ihåg är att iOS 9, som standard, anger shouldDeleteInaccessibleFaults till Sann. Det betyder att ett otillgängligt hanterat objekt markeras som borttaget och dess egenskaper är nollställda. Om shouldDeleteInaccessibleFaults är satt till falsk, Core Data återgår till det gamla beteendet genom att göra ett undantag.

Självklart är det shouldDeleteInaccessibleFaults fastighet går hand i hand med shouldHandleInaccessibleFault (_: forObjectID: triggeredByProperty :). Om du åsidosätter denna metod kan du hantera otillgängliga objekt mer elegant.

Hanteringsfel

När du stöter på ett undantag på grund av att Core Data inte kan uppfylla ett fel kan du tro att Core Data är lite aggressiv. Denna reaktion är ganska vanlig för människor som är nya i ramverket. Sanningen är att utvecklaren har fel. Core Data designades med en specifik uppsättning mål i åtanke och fel är en viktig komponent för att uppnå dessa mål.

Trots deras namn bör fel alltid vara uppfyllbara. Om ett fel inte kan uppfyllas betyder det att datamodellen inte är korrekt inställd eller att applikationen inte respekterar de regler som anges i Core Data-ramverket.

I Core Data Programmering Guide listar Apple ett antal scenarier som kan leda till fel som inte längre kan uppfyllas. De vanligaste scenarierna är felaktigt konfigurerade relationer (radera regler) och radering av ett hanterat objekt medan applikationen fortfarande har en stark referens till det hanterade objektet.

Varnas, det finns andra möjliga scenarier. Från mina erfarenheter, utvecklas utvecklare ofta till liknande problem på grund av ett problem med Core Data stacken. Om programmet till exempel håller en stark referens till ett hanterat objekt och på något sätt avallokerar det hanterade objektets kontext för det hanterade objektet, kan Core Data inte längre uppfylla några fel för det hanterade objektet. Jag hoppas du förstår att detta inte ska hända om du spelar med Core Datas regler.

Slutsats

Kärnfel är oerhört användbara och en viktig del av Apples persistensramverk. Jag hoppas att den här artikeln har lärt dig hur du hanterar fel och var du ska leta efter om du stöter på fel som inte kan uppfyllas. Om du har några frågor, var god att lämna dem i kommentarerna nedan eller kontakta mig på Twitter.