Hva er en Core Data Fault?

Feil er en viktig del av kjerne data. Selv om begrepet høres uheldig, er feilene iboende av livscyklusen til en Core Data-post. I denne opplæringen lærer du hvilke feil som er, hvordan du håndterer dem og hvordan du feilsøker problemer knyttet til feil.

Forutsetninger

Kjernedata er et avansert emne, så jeg antar at du allerede er kjent med Xcode og IOS-utvikling. Selv om jeg skal bruke Swift programmeringsspråket for denne opplæringen, er alt i denne opplæringen også aktuelt for Objective-C.

Wat er en feil?

Core Data er veldig bra på hva det gjør takket være det harde arbeidet i Apples Core Data team. Core Data er svært optimalisert for å holde det lave minnekortet lavt uten å ofre ytelsen. Feil er en av de teknikkene Core Data bruker for å konsumere så lite minne som mulig.

Feil er ikke unikt for Core Data. En lignende teknikk brukes i mange andre rammer, som Ember og Ruby on Rails. Ideen er enkel, bare last inn data når det trengs. For å gjøre feil, gjør Core Data litt magi under hetten ved å lage egendefinerte underklasser på kompileringstid som representerer feilene. La oss se hvordan dette fungerer med et eksempel.

Jeg har laget et enkelt eksempelprogram for å jobbe med. Last ned Xcode-prosjektet fra GitHub og åpne det i Xcode. Prosjektet bruker Swift 2.1, noe som betyr at du trenger Xcode 7.1 eller høyere for å tilfredsstille kompilatoren.

Datamodellen inneholder to enheter, Liste og Punkt. En liste kan ha null eller flere elementer, og et element er alltid knyttet til en liste. Det er et klassisk eksempel på et til mange forhold.

Kjør programmet og fyll ut den vedvarende butikken, en SQLite-database, med noen data ved å lage noen lister og elementer. Avslutt programmet når du er ferdig.

Feilfeil

Du bør nå ha et program med noen data. La oss se hvordan feil fungerer i praksis ved å legge til noen utskriftserklæringer. Åpen ListsViewController.swift og se etter prepareForSegue (_: avsender :). I denne metoden henter vi listen som brukeren har valgt i tabellvisningen. Uansett skrivesettene i prepareForSegue (_: avsender :) som vist i implementeringen nedenfor.

overstyr func prepareForSegue (segue: UIStoryboardSegue, sender: AnyObject?) if segue.identifier == SegueListViewController guard la indexPath = tableView.indexPathForSelectedRow else return // Hent listeliste = self.fetchedResultsController.objectAtIndexPath (indexPath) som! Skriv ut ("1: \ (liste)") hvis la element = list.items print ("2: \ (items)") skriv ut ("3: \ (items.count)") (poster) ") ​​hvis la element = items.anyObject () print (" 5: \ (item) ") skriv ut (" 6: \ (item.name) ")  print ("8: \ (list)") / Hent destinasjonsvisningskontrolleren la listViewController = segue.destinationViewController as! ListViewController // Konfigurer visningskontrollisten listViewController.list = list

Kjør programmet og trykk på en av lister i listervisningskontrollen. Eksemplet er bare interessant hvis du trykker på en liste som har en eller flere elementer knyttet til den. La oss inspisere utskriftene av utskriftene trinn for trinn.

1:  (enhet: Liste; id: 0xd0000000001c0000  ; data: items = ""; navn =" Liste 6 ";)

Det første å merke seg er klassenavnet, Faulting.List. Dette er hva vi forventer siden modulen heter forkastninger og klassen er oppkalt Liste. I Swift består hele klassenavnet til en klasse av modulens navn og klassens navn.

Utgangen i konsollen viser også navnet på enheten, Liste, og en ordbok av data. De Navn Attributtet er satt til Liste 6 mens elementer Attributtet er merket som en forholdsfeil. Dette er den første typen feil i Core Data. Kjernedata forstår at det ikke er behov for å laste forholdet. I stedet merker forholdet som en feil. Den andre utskriftserklæringen bekrefter dette som du kan se nedenfor.

2: Forholdsrelaterte "feil" på administrert objekt (0x154e5d0b0)  (enhet: Liste; id: 0xd0000000001c0000  ; data: items = ""; navn =" Liste 6 ";)

Den tredje utskriftserklæringen er imidlertid mer interessant. Den skriver ut antall elementer som er knyttet til listen.

3: 2

Kjerne data kan bare gjøre dette ved å avbryte forholdet feil. Den spør den vedvarende butikken for elementene som er knyttet til listen. Dette er imidlertid bare en del av historien som illustrert av den fjerde trykte uttalelsen.

4: Relasjonselementer på administrert objekt (0x154e5d0b0)  (enhet: Liste; id: 0xd0000000001c0000  ; data: items = ("0xd000000000540002 "," 0xd000000000580002 "); name =" List 6 ";) med objekter (  (enhet: Element; ID: 0xd000000000540002  ; data: ),  (enhet: Element; ID: 0xd000000000580002  ; data: ))

Vi ser ikke lenger en relasjonsfeil, men vi ser fortsatt en feil. Hva handler det om? Kjernedata kan bare gi oss antall gjenstander for listen ved å skyte eller løse forholdsfeilen. Dette betyr imidlertid ikke at Core Data løser gjenstandene i forholdet. Utgangen i konsollen bekrefter dette.

Vi kan se at postene for elementene er der, inkludert identifikatoren Core Data bruker internt. Dataordlisten er imidlertid merket som en feil. Igjen gir Core Data bare oss det vi ber om. Heldigvis håndteres nitty gritty detaljer av Core Data.

La oss grave litt dypere og hente ett av elementene fra listen. Vi gjør dette ved å ringe anyObject ()elementer gjenstand. Vi skriver ut det resulterende elementet i den femte utskriftserklæringen.

5:  (enhet: Element; ID: 0xd000000000540002  ; data: )

Utgangen bør ikke overraske deg. Utgangen bekrefter at vi har å gjøre med en Punkt enhet. Overraskende er datalogglisten fortsatt merket som en feil. I den sjette utgaven, skriver vi ut Navn attributt av elementet.

6: Element 0

Fordi vi ber om verdien av et attributt til posten, brenner Core Data feilen for å gi oss den verdien. Den henter dataene for elementet og fyller dataordboken. Den syvende utskriftserklæringen bekrefter disse funnene.

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

Datakataloget inneholder Navn attributt så vel som liste forhold. Den åttende og siste utskriftserklæringen viser at forholdets feil i liste objektet er løst.

8:  (enhet: Liste; id: 0xd0000000001c0000  ; data: items = ("0xd000000000540002 "," 0xd000000000580002 "); navn =" Liste 6 ";)

Kan ikke oppfylle en feil

Jeg bestemte meg for å skrive denne artikkelen for å forklare et problem som mange utviklere som bruker Core Data, kjører inn på et eller annet tidspunkt, skyter en feil som ikke kan oppfylles. Problemet er enkelt. La oss anta at du har en liste med en rekke elementer, og på et tidspunkt sletter brukeren listen. Hva skjer med elementene i den listen? Hva skjer hvis Core Data forsøker å brenne liste forholdet til ett av elementene som tilhørte den listen? La oss finne det ut.

La oss se på prosjektet du har lastet ned fra GitHub. Åpen Faulting.xcdatamodeld, prosjektets datamodell. Velg elementer forholdet til Liste enhet og åpne Datamodellinspektør til høyre. Det som er av interesse for oss, er Slett regel, som for øyeblikket er satt til Cascade. Dette betyr at hvert element i listen slettes når listen slettes. Dette gir mening fordi vi ikke vil ha forlatt elementer som ikke er knyttet til en liste.

Velg liste forholdet til Punkt enhet og åpne Datamodellinspektør. De Slett regel av dette forholdet er satt til opphever. Dette betyr at destinasjonen til forholdet, listobjektet, er satt til null når destinasjonsrekorden, elementet, slettes. Dette er standard slettingsregelen for et forhold.

Kjør programmet, opprett noen lister og elementer, og trykk på elementer knappen øverst til venstre for å vise hvert element du har opprettet. Tap Avbryt Øverst til venstre, slett en av lister, og trykk på elementer knappen igjen for å se hva som er endret. Som forventet er også elementene knyttet til den slettede listen slettet av Core Data. Dette er ingen magi. Kjernedata utfører bare de slettereglene vi definerte i datamodellen.

Vi kan konkludere med at forholdene er satt opp riktig for denne typen søknad. Men hva skjer hvis vi ikke konfigurerer slettingsreglene riktig. Åpne datamodellen og sett slettregelen for begge relasjonene, elementer og liste, til Ingen handling. Start programmet på nytt og opprett et par lister og elementer. Hvis du sletter en liste og klikker på elementer knappen øverst til venstre for å se hvert element i den vedvarende butikken, bør elementene som er knyttet til den slettede listen fortsatt være der. De ble ikke slettet, selv om listen de tilhører, er fjernet.

Trykk på en av lister og se hva som skjer. Hvis du kjører programmet på iOS 8, vil programmet krasje på grunn av et uncaught unntak. Hvis du kjører programmet på iOS 9, vil du bare se en feil i konsollen. Stopp riper på hodet og la oss finne ut dette sammen.

Objekt utilgjengelig

Selv om programmet krasjer på iOS 8, er utgangen vi ser i konsollen klar og til poenget.

Feilende [7189: 2427906] *** Avslutte app på grunn av uncaught unntak 'NSObjectInaccessibleException', grunn: 'CoreData kunne ikke oppfylle en feil for' 0x175b2ba0 "

Det første du må legge merke til er at søknaden krasjet på grunn av et uncaught unntak. Det som er viktigere for vår diskusjon, er imidlertid grunnen til at unntaket ble kastet. Kjernedata forteller oss at det ikke var mulig å oppfylle en feil for et forhold. Forholdet Core Data refererer til er liste forholdet til elementet vi tappet i tabellvisningen.

Hvis du ser på implementeringen av Tableview (Tableview: didSelectRowAtIndexPath :), da vil du forstå hvorfor Core Data kastet et unntak. I denne metoden henter vi elementet som brukeren tappet og skriver ut navn på listen til konsollen.

func tableView (tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) tableView.deselectRowAtIndexPath (indexPath, animert: true) hvis la element = self.fetchedResultsController.objectAtIndexPath (indexPath) som? Item print (item.list? .Name)

Fordi det liste forholdet er en feil, Core Data må brenne feilen for å løse den. Dette betyr at Core Data spør den vedvarende butikken for listeposten. Problemet er at posten ikke lenger er i den vedvarende butikken, og derfor ble et unntak kastet.

Slett utilgjengelige feil

Frem til iOS 9 har dette alltid vært standard oppførsel. IOS 9 kaster ikke lenger et unntak fra iOS 9. I stedet logger det en kryptisk melding til konsollen. Til tross for at meldingen er uklar, inneholder den fortsatt grunnen til problemet.

Feil [2806: 1306995] CoreData: advarsel: En NSManagedObjectContext delegat overrode feilhåndteringsadferd for å slette objektet med ID '0xd000000000140000 'og erstatt null / 0 for alle eiendomsverdier i stedet for å kaste.

Hovedpunktet er at Core Data ikke lenger kaster et unntak når det ikke klarer å oppfylle en feil. Den noe gode nyheten er at søknaden din ikke lenger krasjer hvis du ikke får unntaket.

Årsaken til at det ikke kastes et unntak på iOS 9 skyldes innføring av en ny eiendom, shouldDeleteInaccessibleFaults, og en ny metode, shouldHandleInaccessibleFault (_: forObjectID: triggeredByProperty :), på NSManagedObjectContext klasse. Jeg vil ikke dekke disse tilleggene i denne opplæringen.

Det du trenger å huske er at iOS 9 som standard angir shouldDeleteInaccessibleFaults til ekte. Dette betyr at et utilgjengelig administrert objekt er merket som slettet og dets egenskaper nullstilles. Hvis shouldDeleteInaccessibleFaults er satt til falsk, Kjernedata vender tilbake til den gamle oppførselen ved å kaste et unntak.

Selvfølgelig, den shouldDeleteInaccessibleFaults Eiendom går hånd i hånd med shouldHandleInaccessibleFault (_: forObjectID: triggeredByProperty :). Hvis du overstyrer denne metoden, kan du håndtere utilgjengelige objekter mer elegant.

Håndtering av feil

Når du opplever et unntak på grunn av at Core Data ikke klarer å oppfylle en feil, kan du tenke at Core Data er litt aggressiv. Denne reaksjonen er ganske vanlig for folk som er nye i rammen. Sannheten er at utvikleren er i feil. Core Data ble designet med et bestemt sett med mål i tankene og feil er en viktig komponent for å realisere disse målene.

Til tross for deres navn bør feil alltid være oppfylt. Hvis en feil ikke kan oppfylles, betyr det at datamodellen ikke er konfigurert riktig, eller at programmet ikke overholder reglene som er fastsatt av Core Data-rammene.

I hoveddataprogrammeringsguiden viser Apple en rekke scenarier som kan føre til feil som ikke lenger kan oppfylles. De vanligste scenariene er feil konfigurert relasjoner (slett regler) og sletting av et administrert objekt mens programmet fortsatt har en sterk referanse til det administrerte objektet.

Vær advart, det er andre mulige scenarier. Fra min erfaring løper utviklere ofte inn i lignende problemer på grunn av et problem med Core Data-stakken. For eksempel, hvis applikasjonen holder en sterk referanse til et administrert objekt, og på et eller annet tidspunkt deallokerer den administrerte objektkonteksten til det administrerte objektet, kan Core Data ikke lenger oppfylle eventuelle feil for det administrerte objektet. Jeg håper du forstår at dette ikke skal skje hvis du spiller av Core Datas regler.

Konklusjon

Kernedatafeil er utrolig nyttig og en nøkkelkomponent i Apples persistensramme. Jeg håper denne artikkelen har lært deg hvordan du skal håndtere feil og hvor du skal lete etter hvis du går inn i feil som ikke kan oppfylles. Hvis du har noen spørsmål, vær så snill å legge dem i kommentarene nedenfor eller nå ut til meg på Twitter.