Kjernen data fra Scratch: Relasjoner og Mer Fetching

Core data fra Scratch: Relasjoner og Mer Henter
31
Del
Del
Del

Denne Cyber ​​Monday Envato Tuts + kurs vil bli redusert til bare $ 3. Ikke gå glipp av
Dette innlegget er en del av en serie kalt kjernedata fra Scratch.Core data fra scratch. Managed Objects og Fetch RequestsCore data fra Scratch: NSFetchedResultsController

I forrige artikkel, lærte vi om NSManagedObject og hvor enkelt det er å opprette, lese, oppdatere og slette poster ved hjelp av kjernedata. Men det gjorde jeg ikke nevne relasjoner i den diskusjonen. Bortsett fra noen advarsler du må være klar over, relasjoner er like lett å manipulere som attributter. I denne artikkelen vil vi fokusere på relasjoner, og vi vil også fortsette vår utforskning av NSFetchRequest.

1. Relasjoner

Vi har allerede jobbet med relasjoner i Core datamodell redaktør og hva jeg skal fortelle deg vil derfor høres kjent. Relasjoner er, akkurat som attributter, nås ved hjelp av nøkkel-verdi koding. Husk at datamodellen vi opprettet tidligere i denne serien definerer en Person
enhet og en Adresse
enhet. En person som er knyttet til en eller flere adresser, og en adresse er knyttet til en eller flere personer. Dette er en mange-til-mange
forholdet

For å hente adressene til en person, vi bare påberope valueForKey:. På person, en forekomst av NSManagedObject, og pass på adresser som nøkkel. Merk at adresser er nøkkelen vi definert i datamodellen. Hva slags objekt du forvente? De fleste nye til Core data forvente en sortert NSArray, men kjernedata returnerer en NSSet, som usortert. Arbeide med NSSet har sine fordeler som du vil lære senere.

Opprette Records

Nok med teorien, åpne prosjektet fra forrige artikkel eller klone det fra GitHub. La oss begynne med å lage en person og deretter koble den til en adresse. For å opprette en person, oppdatere programmet: didFinishLaunchingWithOptions: metode som vist nedenfor Anmeldelser - (BOOL) program:. (UIApplication *) applikasjons didFinishLaunchingWithOptions: (NSDictionary *) launchOptions {//Initial Window self.window = [[UIWindow alloc ] initWithFrame: [[UIScreen mainScreen] grenser]]; //Konfigurer Window [self.window setBackgroundColor: [UIColor whiteColor]]; [self.window makeKeyAndVisible]; //Opprett Person NSEntityDescription * entityPerson = [NSEntityDescription entityForName: @ "person" inManagedObjectContext: self.managedObjectContext]; NSManagedObject * newPerson = [[NSManagedObject alloc] initWithEntity: entityPerson insertIntoManagedObjectContext: self.managedObjectContext]; //Set Først og lats Navn [newPerson SetValue: @ "Bart" Forkey: @ "første"]; [newPerson SetValue: @ "Jacobs" Forkey: @ "siste"]; [newPerson SetValue: @ 44 Forkey: @ "age"]; returnere JA;}

Dette bør se kjent hvis du har lest den forrige artikkelen. Opprette en adresse ligner som du kan se nedenfor
//Opprett AddressNSEntityDescription * entityAddress = [NSEntityDescription entityForName: @ "adresse" inManagedObjectContext: self.managedObjectContext];. NSManagedObject * newAddress = [[NSManagedObject alloc] initWithEntity: entityAddress insertIntoManagedObjectContext: self.managedObjectContext]; //Set Først og etternavn [newAddress SetValue: @ "Main Street" Forkey: @ "street"] [newAddress SetValue: @ "Boston" Forkey: @ "by"];

Fordi hver egenskap av Adresse
enhet er markert som valgfritt
, trenger vi ikke å tilordne en verdi til hvert attributt. I eksempelet ovenfor, kan vi bare sette rekord er gate- og attributter

Opprette et forhold

Hvis du vil koble newAddress til newPerson, påberope vi SetValue. Forkey :, passerer adresser som nøkkel. Verdien som vi passerer i en NSSet som inneholder newAddress. Ta en titt på følgende kodeblokken for avklaring
//Legg til adresse til person [newPerson SetValue: [NSSet setWithObject: newAddress] Forkey: @ "adresser"];. //Lagre Managed Object ContextNSError * error = null, hvis (! [newPerson.managedObjectContext sparer: & error]) {NSLog (@ "Kan ikke lagre klarte objekt sammenheng."); NSLog (@ "% @,% @", feil, error.localizedDescription);}

Vi kaller spare: på greid objekt sammenheng med newPerson objekt å forplante endringene i vedvarende butikken. Husk at kallet spare: på en administrert objekt sammenheng sparer staten forvaltet objektet sammenheng. Dette betyr at newAddress er også skrevet for å støtte butikken samt relasjonene vi bare definert.

Du lurer kanskje på hvorfor vi ikke knytte newPerson til newAddress, fordi vi gjorde definere en invers sammenheng i våre data modell. Kjernen data skaper dette forholdet for oss. Hvis et forhold har en invers sammenheng, da kjernedata tar seg av dette automatisk. Du kan kontrollere dette ved å spørre newAddress objekt for sine personer.

Henter og oppdatere en Relationship

Oppdatering et forhold er ikke vanskelig heller. Det eneste forbeholdet er at vi må legge til eller fjerne elementer fra uforanderlige NSSet eksempel kjernedata hender til oss. For å gjøre denne oppgaven enklere, men erklærer NSManagedObject en praktisk metode mutableSetValueForKey :, som returnerer en NSMutableSet objekt. Vi kan da bare legge til eller fjerne et element fra samlingen for å oppdatere forholdet.

Ta en titt på følgende kode blokken der vi skaper en annen adresse, og forbinder det med newPerson. Vi gjør dette ved å påberope mutableSetValueForKey: på newPerson og legge otherAddress til foranderlig sett. Det er ikke nødvendig å fortelle kjernedata som vi har oppdatert forholdet. Kjernen data holder styr på foranderlig sett at det ga oss og oppdaterer forholdet tilsvar
//Opprett AddressNSManagedObject * otherAddress = [[NSManagedObject alloc] initWithEntity: entityAddress insertIntoManagedObjectContext: self.managedObjectContext];. //Set Først og etternavn [otherAddress SetValue: @ "5th Avenue" Forkey: @ "street"] [SetValue otherAddress: @ "New York" Forkey: @ "by"]; //Legg til adresse til PersonNSMutableSet * adresser = [newPerson mutableSetValueForKey: @ "adresser "] [adresser addObject: otherAddress];
Slette et forhold

Slette et forhold er så enkelt som å påkalle SetValue: Forkey :, passerer null som verdien og navnet på forholdet som nøkkelen . Dette unlinks hver adresse fra newPerson
//Slett Sivil [newPerson SetValue: nil Forkey: @ "adresser"];.
2. En-til-en og en-til-mange relasjoner

En-til-en-relasjoner

Selv om vår datamodell ikke definerer et en-til-en-relasjon, har du lært alt du trenger å vite for å jobbe med denne type forhold. Arbeider med en en-til-en forhold er identisk til å samarbeide med attributter. Den eneste forskjellen er at verdien du kommer tilbake fra valueForKey: og verdien du sender til SetValue: Forkey: er en NSManagedObject eksempel

La oss oppdatere våre datamodell for å illustrere dette.. Åpne Core_Data.xcdatamodeld Hotell og velg Person
enhet. Opprett et nytt forhold og gi den navnet ektefelle
. Sett Person
enhet som destinasjon og sette ektefelle
forhold som den inverse forholdet.

Som du kan se, er det fullt mulig å skape en relasjon der målet for forholdet er samme enhet som den enhet som definerer forholdet. Merk også at vi alltid satt den inverse av forholdet. Som dokumentasjon statene, er det svært få situasjoner der du ønsker å skape et forhold som ikke har en invers sammenheng.

Vet du hva som vil skje hvis du skulle bygge og kjøre programmet? Det er riktig, vil programmet krasje. Fordi vi endret datamodellen, den eksisterende butikken, en SQLite database i dette eksempelet, er ikke lenger forenlig med datamodellen. For å bøte på dette, fjerne programmet fra enheten eller iOS Simulator og kjøre programmet. Ikke bekymre deg om, vil vi løse dette problemet mer elegant i en fremtidig avdrag hjelp vandringer.

Hvis du kan kjøre programmet uten problemer, så er det tid for neste trinn. Head tilbake til programmet delegat klassen og legge til følgende kode blokken
//Opprett en PersonNSManagedObject * anotherPerson = [[NSManagedObject alloc] initWithEntity: entityPerson insertIntoManagedObjectContext: self.managedObjectContext];. //Set Først og etternavn [anotherPerson SetValue : @ "Jane" Forkey: @ "første"] [anotherPerson SetValue: @ "Doe" Forkey: @ "siste"] [anotherPerson SetValue: @ 42 Forkey: @ "alder"];

Hvis du vil angi anotherPerson som ektefelle newPerson, vi påberope SetValue: Forkey: på newPerson og passere i anotherPerson og @ "ektefelle" som argumenter. Vi kan oppnå samme resultat ved å påberope SetValue: Forkey: på anotherPerson og bestått i newPerson og @ "ektefelle" som argumenter
//Opprett et forhold. [NewPerson SetValue: anotherPerson Forkey: @ "ektefelle"];
En-til-mange relasjoner

La oss avslutte med en titt på en-til-mange-relasjoner. Åpne Core_Data.xcdatamodeld
, velg Person
enhet, og skape en relasjon med navnet barn
. Sett reisemålet til Person
, angi typen til Slik Mange
, og la den inverse forholdet tom for nå.

Opprett et annet forhold som heter far
, angi dette som reisemål til Person
, og sette den inverse forholdet til barn
. Dette vil automatisk fylle den inverse forholdet mellom barn
forholdet vi tom for et øyeblikk siden. Vi har nå laget en en-til-mange-relasjon, det vil si en far kan ha mange barn, men et barn kan bare ha én far.

Leder tilbake til programmet delegat og legge til følgende kode blokken . Vi skaper en annen Person
posten, angi attributter, og sett det som et barn av newPerson ved å spørre Kjerne Data for et foranderlig sett for nøkkel barn og legge den nye plata til foranderlig sett. Twitter //Opprett et barn PersonNSManagedObject * newChildPerson = [[NSManagedObject alloc] initWithEntity: entityPerson insertIntoManagedObjectContext: self.managedObjectContext]; //Set Først og etternavn [newChildPerson SetValue: @ "Jim" Forkey: @ "første"] [newChildPerson SetValue: @ "Doe" Forkey: @ "siste"] [newChildPerson SetValue: @ 21 Forkey: @ "age"]; //Opprett RelationshipNSMutableSet * barn = [newPerson mutableSetValueForKey: @ "barn"]; [barn addObject: newChildPerson];

Følgende kode blokk oppnår samme resultat ved å sette far egenskap av anotherChildPerson. Resultatet er at newPerson blir far til anotherChildPerson og anotherChildPerson blir et barn av newPerson
//Opprett en Child PersonNSManagedObject * anotherChildPerson = [[NSManagedObject alloc] initWithEntity: entityPerson insertIntoManagedObjectContext: self.managedObjectContext];. //Set Først og etternavn [anotherChildPerson SetValue: @ "Lucy" Forkey: @ "første"] [anotherChildPerson SetValue: @ "Doe" Forkey: @ "siste"] [anotherChildPerson SetValue: @ 19 Forkey: @ "age"]; //Lag Sivil [anotherChildPerson SetValue: newPerson forKeyPath: @ "far"];
3. Mer Henter

Datamodellen av vår prøveprogrammet har vokst ganske mye i form av kompleksitet. Vi har opprettet en-til-en, en-til-mange og mange-til-mange relasjoner. Vi har sett hvor enkelt det er å lage poster, inkludert relasjoner. Men hvis vi ønsker også å være i stand til å trekke at data fra den vedvarende butikken, så vi trenger å vite mer om henting. La oss starte med et enkelt eksempel der ser vi hvordan å sortere resultatene som returneres av en hente forespørsel.

Sorter beskrivelsene

Hvis du vil sortere postene vi får tilbake fra greid objektet sammenheng, bruker vi den NSSortDescriptor klassen. Ta en titt på følgende kodebiten
//FetchingNSFetchRequest * fetchRequest = [[NSFetchRequest alloc] initWithEntityName: @ "Person"]; //Legg Sorter DescriptorNSSortDescriptor * sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:. @ "Første" stigende: JA ] [fetchRequest setSortDescriptors: @ [sortDescriptor]]; //Execute Fetch RequestNSError * fetchError = null; NSArray * resultat = [self.managedObjectContext executeFetchRequest: fetchRequest error: & fetchError]; if (! fetchError) {for (NSManagedObject * managedObject i resultat) {NSLog (@ "% @,% @", [managedObject valueForKey: @ "første"], [managedObject valueForKey: @ "siste"]); }} Else {NSLog (@ "Feil ved henting av data."); NSLog (@ "% @,% @", fetchError, fetchError.localizedDescription);}

Vi initial en henteforespørsel ved å sende i foretaket som vi er interessert i, Person
. Vi deretter opprette en NSSortDescriptor objekt ved å påberope sortDescriptorWithKey: stigende :, bestått i egenskap av foretaket vi ønsker å sortere etter, først, og en boolsk angir om postene må sorteres i stigende eller synkende rekkefølge
.

Vi knytte slags descriptor til hente forespørsel ved å påberope setSortDescriptors: på hente forespørsel, passerer i en matrise som inneholder den typen beskrivelse. Fordi setSortDescriptors: godtar en matrise, er det mulig å passere i mer enn ett slags beskrivelse. Vi vil ta en titt på dette alternativet i et øyeblikk.

Resten av kodeblokken bør se kjent. Hente forespørselen sendes til den administrerte objektet sammenheng, som utfører hente forespørsel når vi påberope executeFetchRequest: error :. Det er viktig å alltid passere i en peker til et NSError objekt for å vite hva som gikk galt hvis gjennomføringen av hente forespørsel mislykkes.

Kjør programmet og inspisere produksjonen i Xcode konsoll. Utgangen bør ligne på det som vises nedenfor. Som du kan se, er postene sorteres etter fornavn
kjernedata. [1 080: 613] Bart, JacobsCore data [1 080: 613] Jane, DoeCore data [1 080: 613] Jim, DoeCore data [1 080: 613 ] Lucy, Doe

Hvis du ser duplikater i produksjonen, så sørg for å kommentere ut koden vi skrev tidligere for å skape postene. Hver gang du kjører programmet, er de samme postene opprettet, noe som resulterer i like poster.

Som jeg nevnte tidligere, er det mulig å kombinere flere sorterings beskrivelsene. La oss sortere postene ved deres siste Anmeldelser navn og alder
. Vi først sette nøkkelen i den første slags descriptor å vare. Vi deretter opprette en annen slags descriptor med en nøkkel av alder og legge den til i rekke sorterings beskrivelser som vi passerer til setSortDescriptors :.
//Legg Sorter DescriptorNSSortDescriptor * sortDescriptor1 = [NSSortDescriptor sortDescriptorWithKey: @ "siste" stigende: YES]; NSSortDescriptor * sortDescriptor2 = [NSSortDescriptor sortDescriptorWithKey: @ "age" stigende: YES] [fetchRequest setSortDescriptors: @ [sortDescriptor1, sortDescriptor2]];

Utgangen viser at rekkefølgen på sorterings beskrivelsene i matrisen er viktig. Postene er først sortert etter deres etternavn og deretter av deres alder
kjernedata. [1418: 613] Lucy, Doe (19) Kjerne data [1418: 613] Jim, Doe (21) Kjerne data [1418: 613 ] Jane, Doe (42) Kjerne data [1418: 613] Bart, Jacobs (44)
predikater

Sorter beskrivelsene er stor og lett å bruke, men predikater er det som virkelig gjør henting kraftig i kjernedata . Mens sorterings beskrivelsene forteller kjernedata hvordan postene må sorteres, predikater fortelle den hva registrerer du er interessert i. Den klassen vi skal jobbe med er NSPredicate.

La oss starte med å hente hvert medlem av Doe familien. Dette er veldig enkelt å gjøre og syntaksen vil minne noen av dere av SQL
//FetchingNSFetchRequest * fetchRequest = [[NSFetchRequest alloc] initWithEntityName: @ "Person"]; //Opprett PredicateNSPredicate * predikat = [NSPredicate predicateWithFormat.: @ "% K ==% @", @ "siste", @ "Doe"] [fetchRequest setPredicate: predikat]; //Legg Sorter DescriptorNSSortDescriptor * sortDescriptor1 = [NSSortDescriptor sortDescriptorWithKey: @ "siste" stigende: YES]; NSSortDescriptor * sortDescriptor2 = [NSSortDescriptor sortDescriptorWithKey: @ "age" stigende: YES] [fetchRequest setSortDescriptors: @ [sortDescriptor1, sortDescriptor2]]; //Execute Fetch RequestNSError * fetchError = null; NSArray * resultat = [self.managedObjectContext executeFetchRequest: fetchRequest feil : & fetchError], hvis {for (NSManagedObject * managedObject i resultat) {NSLog (@ "% @,% @", [managedObject valueForKey (fetchError!): @ "første"], [managedObject valueForKey: @ "siste" ]); }} Else {NSLog (@ "Feil ved henting av data."); NSLog (@ "% @,% @", fetchError, fetchError.localizedDescription);}

Vi har ikke forandret seg mye bortsett fra å lage en NSPredicate objekt ved å påberope predicateWithFormat: og knytte predikatet til hente forespørsel ved å sende det som et argument for en setPredicate: ring. Ideen bak predicateWithFormat: ligner stringWithFormat: i at den godtar et variabelt antall argumenter

Merk at predikatet formatstrengen bruker% K for eiendommen navn og% @ for verdien.. Som nevnt i Predicate Programming Guide, er% K en variabel argument erstatning for en nøkkelbanen mens% @ er en variabel argument erstatning for et objekt verdi. Dette betyr at predikatet format streng med vårt eksempel vurderer å vare == "Doe"

Hvis du kjører programmet en gang til og inspisere produksjonen i Xcode konsollen, bør du se følgende resultat:.
kjernedata [1582: 613] Lucy, Doe (19) Kjerne data [1 582: 613] Jim, Doe (21) Kjerne data [1 582: 613] Jane, Doe (42)

Det er mange aktører vi kan bruke for sammenligning. I tillegg til = og ==, som er identiske så langt som kjernedata er opptatt av, det er også > = og = >, < = og = >, = og <! ≫, og > og <. Jeg oppfordrer deg til å eksperimentere med disse aktørene for å lære hvordan de påvirker resultatene av hente forespørsel

Følgende predikat illustrerer hvordan vi kan bruke >. = Operatøren å bare hente Person poster med en alder attributt større enn 30.
NSPredicate * predikat = [NSPredicate predicateWithFormat: @ "% K > =% @", @ "alder", @ (30)];

Vi har også operatører for streng sammenligning INNEHOLDER, SOM, KAMPER, BEGINSWITH, og ENDSWITH. La oss hente hver Person posten hvis navn inneholder bokstaven j
NSPredicate * predikat =. [NSPredicate predicateWithFormat: @ "% K INNEHOLDER% @", @ "første", @ "j"];

Hvis du kjører søknad nå, vil rekken av resultatene være tom siden strengen sammenligningen er store og små bokstaver som standard. Vi kan endre dette ved å legge til en endrings som så:
NSPredicate * predikat = [NSPredicate predicateWithFormat: @ "% K INNEHOLDER [c]% @", @ "første", @ "j"];

Du kan også lage sammensatte predikater ved hjelp av stikkorda AND, OR og NOT. I følgende eksempel, vi hente hver person med fornavn inneholder bokstaven j og er yngre enn 30.
NSPredicate * predikat = [NSPredicate predicateWithFormat: @ "% K INNEHOLDER [c]% @ og% K < 30" @ "første", @ "j", @ "alder", @ (30)];

predikater også gjør det svært enkelt å hente poster basert på forholdet deres. . I følgende eksempel, vi hente hver person som fars navn er lik Bart
NSPredicate * predikat = [NSPredicate predicateWithFormat: @ "% K ==% @", @ "father.first", @ "Bart"];

Listen predikat fungerer som forventet, fordi% K er en variabel argument erstatning for en -tasten banen
, ikke bare en -tasten

Hva du trenger. å huske er at predikater gjøre deg i stand til å spørre kompet butikken uten at du vet noe om butikken. Selv om syntaksen til predikatet formatstrengen minner om SQL på noen måter, det spiller ingen rolle om backing butikken er en SQLite database eller en in-memory butikken. Dette er en svært kraftig konsept som ikke er unik for kjernedata. Rails Active Record er et annet godt eksempel på dette paradigmet.

Det er mye mer å predikater enn det jeg har vist deg i denne artikkelen. Hvis du ønsker å lære mer om predikater, foreslår jeg at du tar en kikk på Apples Predicate Programming Guide. Vi vil også jobbe mer med predikater i de neste artiklene i denne serien.

Konklusjon

Vi har nå en god forståelse av det grunnleggende kjernedata, og det er på tide å begynne å jobbe med rammeverk ved å lage et program som utnytter sin makt. I neste artikkel, møter vi en annen viktig klasse av Core data rammeverket, NSFetchedResultsController. Denne klassen vil hjelpe oss å administrere en samling av poster, men du vil lære at det gjør ganske mye mer enn det.