Core data fra Scratch: Managed Objects og Fetch Forespørsler
13
Del
3
Del
Dette Cyber mandag 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:. Data ModelCore data fra Scratch: Relasjoner og Mer Henter
Med alt om Cora data datamodeller fortsatt friskt i tankene dine, er det på tide å begynne å jobbe med kjernedata. I denne artikkelen møter vi NSManagedObject, klassen du vil samhandle med de fleste når du arbeider med kjernedata. Du vil lære å opprette, lese, oppdatere og slette poster.
Du vil også få vite et par andre Kjerne-dataklasser, som NSFetchRequest og NSEntityDescription. La meg starte med å introdusere deg til NSManagedObject, din nye bestevenn.
1. Managed Objects
Forekomster av NSManagedObject representerer en rekord i Core-Datas backing butikken. Husk, det spiller ingen rolle hva som backing butikken ser ut. Men for å dra tilbake til den databasen analogi, inneholder en NSManagedObject eksempel informasjon om en rad i en databasetabell.
Grunnen kjernedata bruker NSManagedObject istedenfor NSObject som sin base klasse for modellering postene vil være mer fornuftig litt seinere. Før vi begynner å arbeide med NSManagedObject, trenger vi å vite noen ting om denne klassen.
NSEntityDescription
Hver NSManagedObject eksempel er assosiert med en forekomst av NSEntityDescription. Foretaket beskrivelse inneholder informasjon om den klarte objekt, for eksempel enhet
av forvaltet objekt samt sin attributter Hotell og relasjoner
.
NSManagedObjectContext
En administrert objekt er også knyttet til en forekomst av NSManagedObjectContext. Den klarte objektet sammenheng som en administrert objekt tilhører, overvåker klarte gjenstand for endringer.
2. Opprette en Record
Med dette i tankene, skaper en administrert objekt er ganske grei. Å sørge for en administrert objekt er riktig konfigurert, er det anbefalt å bruke den utpekte initializer for å skape nye NSManagedObject tilfeller. La oss se hvordan dette fungerer ved å opprette en ny person objekt.
Åpne prosjektet fra forrige artikkel eller klone det fra GitHub. Fordi vi ikke skal bygge et funksjonelt program i denne artikkelen, vil vi gjøre det meste av vårt arbeid i søknaden delegat klassen, TSPAppDelegate. Åpne TSPAppDelegate.m Hotell og oppdatere gjennomføringen av programmet: didFinishLaunchingWithOptions: som vist nedenfor Anmeldelser - (BOOL) program:. (UIApplication *) applikasjons didFinishLaunchingWithOptions: (NSDictionary *) launchOptions {//Initial Window selv .window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] grenser]]; //Konfigurer Window [self.window setBackgroundColor: [UIColor whiteColor]]; [self.window makeKeyAndVisible]; //Opprett Managed Object NSEntityDescription * entityDescription = [NSEntityDescription entityForName: @ "person" inManagedObjectContext: self.managedObjectContext]; NSManagedObject * newPerson = [[NSManagedObject alloc] initWithEntity: entityDescription insertIntoManagedObjectContext: self.managedObjectContext]; returnere JA;}
Det første vi gjør, er å skape en forekomst av NSEntityDescription klassen ved å påberope entityForName: inManagedObjectContext :. Vi passerer navnet av foretaket vi ønsker å skape en administrert objekt for, @ "Person", og en NSManagedObjectContext instans.
Hvorfor trenger vi å passere i en NSManagedObjectContext objekt? Vi spesifiserer navnet som vi ønsker å skape en administrert objekt for, men vi trenger også å fortelle kjernedata hvor den kan finne datamodellen for foretaket. Husk at en administrert objekt sammenheng er knyttet til et vedvarende butikken koordinator og en vedvarende butikken koordinator holder en referanse til en datamodell. Når vi passerer i en administrert objekt sammenheng, spør kjernedata sin vedvarende butikken koordinator for sin datamodell for å finne enheten vi leter etter.
I det andre trinnet, påberope vi utpekt initializer av NSManagedObject klasse , initWithEntity: insertIntoManagedObjectContext :. Vi passerer i foretaket beskrivelse og en NSManagedObjectContext eksempel. Vente? Hvorfor trenger vi å passere i en annen NSManagedObjectContext eksempel? Husk hva jeg skrev tidligere. En administrert objekt er involvert i et aksjeselskap beskrivelse og Selge den lever i en administrert objekt sammenheng, og det er derfor vi fortelle kjernedata som klarte objekt sammenheng den nye greid objekt bør knyttes til.
Dette er ikke for komplisert, er det? Vi har nå opprettet en ny person objekt. Hvordan endrer vi attributtene eller definere et forhold? Dette gjøres ved å utnytte nøkkelverdien koding. Å endre det første navnet på den nye personen objektet vi nettopp opprettet vi følgende
[newPerson SetValue: @ "Bart" Forkey: @ "første"] [newPerson SetValue:. @ "Jacobs" Forkey: @ "last "];
Hvis du er kjent med nøkkelverdien koding, så dette bør se veldig kjent. Fordi NSManagedObject klassen støtter nøkkelverdien koding, endrer vi et attributt ved å påberope SetValue: Forkey :. Det er så enkelt.
En ulempe med denne tilnærmingen er den enkle som du kan introdusere feil ved feilstaving et attributt eller relasjon navn. Også attributtnavnene er ikke autocompleted av Xcode som for eksempel egenskapsnavnene. Vi kan løse dette problemet, men det er noe vi vil ta en titt på litt senere i denne serien.
Før vi fortsetter vår utforskning av NSManagedObject, la oss sette en alder av newPerson til 44. product: [ ,,,0],newPerson SetValue: @ 44 Forkey: @ "alder"];
Hvis du ikke er kjent med nøkkelverdien koding, så du kan bli overrasket over at vi passerte i en NSNumber bokstavelig stedet for et heltall, som vi definert i våre data modell. Den SetValue: Forkey: metode godtar bare objekter, ingen primitiver. Ha dette i bakhodet.
3. Lagre en Record
Selv om vi nå har en ny person eksempel har grunndata lagres ikke personen til sin backing butikken ennå. Den klarte objektet vi skapt bor for tiden i den klarte objekt i hvilken sammenheng det ble satt inn. Å redde personen objektet til backing butikken, trenger vi å lagre endringene av den administrerte objektet sammenheng ved å ringe redning. På den
Den sparer: metoden returnerer en boolsk å indikere resultatet av lagringen og godtar en peker til et NSerror objekt, forteller oss hva som gikk galt hvis lagringen er mislykket. Ta en titt på følgende kodeblokken for avklaring
NSError * error = null, hvis.: {NSLog (@ "Kan ikke lagre klarte objekt sammenheng."); ([NewPerson.managedObjectContext spare & feil]!) NSLog (@ "% @,% @", feil, error.localizedDescription);}
Bygg og kjøre programmet for å se om alt fungerer som forventet. Har du også kjøre inn i en krasj? Hva gjorde i konsollet for deg? Gjorde det ligne utgangs under
Kjerne data [1 218: 38496] *** Avslutte app grunn uoppfanget unntak 'NSInvalidArgumentException', grunn: "Uakseptabelt type verdi for attributtet:? Property =" første "; ønsket type = NSDate; gitt type = __NSCFConstantString; . value = Bart '*** Første kast kallstakken :( 0 Corefoundation 0x01f4d646 __exceptionPreprocess + 182 1 libobjc.A.dylib 0x01bef8e3 objc_exception_throw + 44 2 CoreData 0x00308e6e _PFManagedObject_coerceValueForKeyWithDescription + 3454 3 CoreData 0x002db39d _sharedIMPL_setvfk_core + 205 4 CoreData 0x00308096 - [NSManagedObject (_PFDynamicAccessorsAndPropertySupport ) _setGenericValue: Forkey: withIndex: flagg:] + 54 5 CoreData 0x002f735c _PF_Handler_Public_SetProperty + 108 6 CoreData 0x002f72c5 - [NSManagedObject SetValue: Forkey:] + 181 7 Kjerne data 0x00002beb - [TSPAppDelegate applikasjons: didFinishLaunchingWithOptions:] + 891 8 UIKit 0x0066bb37 - [ ,,,0],UIApplication _handleDelegateCallbacksWithOptions: isSuspended: restoreState:] + 291 9 UIKit 0x0066c875 - [UIApplication _callInitializationDelegatesForMainScene: transitionContext:] + 2920 10 UIKit 0x0066fa33 - [UIApplication _runWithMainScene: transitionContext: ferdigstillelse:] + 1 507 11 UIKit 0x00687eb8 __84- [UIApplication _handleApplicationActivationWithScene: transitionContext: ferdigstillelse :] _ block_invoke + 59 12 UIKit 0x0066e77e - [UIApplication workspaceDidEndTransaction:] + 29 13 FrontBoardServices 0x04264f1f - [FBSWorkspace clientEndTransaction:] + 87 14 FrontBoardServices 0x0426c4ed __53- [FBSWorkspaceClient _queue_handleTransactionBookEnd] _block_invoke + 49 15 Corefoundation 0x01e71f90 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 16 16 Corefoundation 0x01e67133 __CFRunLoopDoBlocks + 195 17 Corefoundation 0x01e66898 __CFRunLoopRun + 936 18 Corefoundation 0x01e6622b CFRunLoopRunSpecific + 443 19 Corefoundation 0x01e6605b CFRunLoopRunInMode + 123 20 UIKit 0x0066e095 - [UIApplication _run] + 571 21 UIKit 0x006716e5 UIApplicationMain + 1526 22 kjernedata 0x0000394d hoved + 141 23 libdyld.dylib 0x0250bac9 starte + 1 ) libc ++ abi.dylib å avslutte med uoppfanget unntak av typen NSException
Xcode forteller oss at det forventes en NSDate eksempel for første attributt, men vi passerte i en NSString. Hvis du åpner Core datamodellen vi skapte i den forrige artikkelen, vil du se at den type første attributt er faktisk Dato
. Endre den til String Hotell og kjøre programmet en gang til.
En annen krasjer? Selv om dette er en mer avansert emne, er det viktig å forstå hva som skjer.
Data Model Kompatibilitet
Utgangen i Xcode konsoll bør ligne utgangs vist nedenfor. Legg merke til at feilen er forskjellig fra den forrige. Xcode forteller oss at modell for å åpne butikken er uforenlig med den som ble brukt for å lage den butikken
. Hvordan skjedde dette
Uløst error Error Domain = NSCocoaErrorDomain Code = 134100 "Handlingen kunne ikke fullføres (Cocoa feil 134100.)." Userinfo = 0xcb17a30 {metadata = {NSPersistenceFrameworkVersion = 508; NSStoreModelVersionHashes = {Adresse = < 268460b1 0507da45 f37f8fb5 b17628a9 a56beb9c 8666f029 4276074d 11160d13 >; Person = < 68eb2a17 12dfaf41 510772c0 66d91b3d 7cdef207 4948ac15 f9ae22cc fe3d32f2 >; }; NSStoreModelVersionHashesVersion = 3; NSStoreModelVersionIdentifiers = (""); NSStoreType = SQLite; NSStoreUUID = "EBB4C708-F933-4E74-8EE0-47F9972EE523"; "_NSAutoVacuumLevel" = 2;}, fornuft = Modellen brukes til å åpne butikken er uforenlig med den som ble brukt for å lage den butikken} {metadata = {NSPersistenceFrameworkVersion = 508; NSStoreModelVersionHashes = {Adresse = < 268460b1 0507da45 f37f8fb5 b17628a9 a56beb9c 8666f029 4276074d 11160d13 >; Person = < 68eb2a17 12dfaf41 510772c0 66d91b3d 7cdef207 4948ac15 f9ae22cc fe3d32f2 >; }; NSStoreModelVersionHashesVersion = 3; NSStoreModelVersionIdentifiers = (""); NSStoreType = SQLite; NSStoreUUID = "EBB4C708-F933-4E74-8EE0-47F9972EE523"; "_NSAutoVacuumLevel" = 2; }; Grunnen = "Modellen brukes til å åpne butikken er uforenlig med den som ble brukt for å lage den store";}
Når vi først lanserte programmet en liten stund siden, Core data inspisert datamodellen, og basert på denne modellen, opprettet en butikk for oss, en SQLite database i dette tilfellet. Kjernen data er flink skjønt. Det gjør at konstruksjonen av støtte butikken og som av datamodellen er kompatible. Dette er viktig å sørge for at vi får tilbake fra underlags butikken hva vi forventer og hva vi legger det i første omgang.
I løpet av den første ulykken, la vi merke til at vår datamodell inneholdt en feil og vi endret typen av den første attributtet fra Dato
til String
. Med andre ord, vi endret datamodellen selv om kjernedata allerede hadde opprettet kompet butikken for oss basert på feil datamodell.
Etter oppdatering av datamodellen, lanserte vi programmet på nytt, og kjørte inn i den andre brak. En av de tingene kjernedata gjør når det skaper Core data stabelen er å sørge for datamodellen og kompet butikk-hvis det finnes-er kompatible. Det var ikke tilfelle i vårt eksempel dermed ulykken.
Hvordan kan vi løse dette? Den enkle løsningen er å fjerne programmet fra enheten eller fra iOS Simulator, og starte programmet på nytt. Men dette er noe du ikke kan gjøre hvis du allerede har en applikasjon i App Store som folk bruker. I så fall må du bruke vandringer, som er noe vi vil diskutere i en senere artikkel.
Fordi vi ikke har millioner av brukere ved hjelp av vår søknad, kan vi trygt fjerne programmet fra vår test enheten og kjøre den en gang til. Hvis alt gikk bra, er den nye personen nå trygt lagret i butikken, SQLite database kjernedata skapt for oss.
Kontroll av Backing butikken
Du kan bekrefte at lagringsoperasjonen jobbet ved ta en titt på innsiden av SQLite database. Hvis du kjørte programmet i iOS Simulator, og deretter navigere til ~ /< bruker > /Bibliotek /Application Support /iPhone Simulator /< versjon > /< OS > /Programmer /< ID > /Dokumenter /Core_Data.sqlite
. For å gjøre livet ditt enklere, anbefaler jeg at du installerer SimPholders, et verktøy som gjør navigeringen til ovennevnte banen mye, mye enklere. Åpne SQLite database og inspisere tabellen navnet ZPERSON
. Tabellen skal ha én oppføring, den vi satt inn et minutt siden.
Du bør ha to ting i tankene. Først, det er ikke nødvendig å forstå databasestrukturen. Kjernen data forvalter backing butikken for oss, og vi trenger ikke å forstå dens struktur for å jobbe med kjernedata. For det andre, aldri åpne butikken direkte. Kjernen data er ansvarlig for backing butikken og vi må respektere at hvis vi ønsker kjernedata for å gjøre jobben sin godt. Hvis vi begynner å samhandle med SQLite database-eller hvilken som helst annen butikk-det er ingen garanti Core-Data vil fortsette å fungere skikkelig. Kort sagt, er kjernedata ansvarlig for butikken så la det være.
4. Henter Records
Selv om vi tar en nærmere titt på NSFetchRequest i neste artikkel, trenger vi NSFetchRequest klassen til å be Kjerne Data for informasjon fra objektet grafen den klarer. . La oss se hvordan vi kan hente posten vi satt inn tidligere bruker NSFetchRequest
NSFetchRequest * fetchRequest = [[NSFetchRequest Alloc] init]; NSEntityDescription * foretakets = [NSEntityDescription entityForName: @ "person" inManagedObjectContext: self.managedObjectContext] [fetchRequest setEntity: enhet]; NSError * error = null; NSArray * resultat = [self.managedObjectContext executeFetchRequest: fetchRequest error: & error]; if (feil) {NSLog (@ ". Kan ikke kjøre hente request"); NSLog (@ "% @,% @", feil, error.localizedDescription); } Else {NSLog (@ "% @", resultat);}
Etter initialisering hente forespørsel, skaper vi en NSEntityDescription objekt og tilordne den til foretaket eiendom hente forespørsel. Som du kan se, bruker vi NSEntityDescription klassen å fortelle kjernedata hva foretaket vi er interessert i
Henter data håndteres av NSManagedObjectContext klasse, påberope vi executeFetchRequest. Error :, bestått i å hente forespørsel og en peker til et NSError objekt. Metoden returnerer en matrise av resultater hvis hente forespørselen er vellykket og null hvis det oppstår et problem. Merk at kjernedata returnerer alltid en NSArray objekt hvis hente forespørselen er vellykket, selv om vi forventer ett resultat eller hvis kjernedata ikke finne noen samsvarende poster.
Kjør programmet og inspisere produksjonen i Xcode konsoll . Nedenfor kan du se hva som ble returnert, en matrise med ett objekt av typen NSManagedObject. Foretaket av objektet er Person
Kjerne data [1 588: 613]. ("≪ NSManagedObject: 0x1094352d0 > (enhet: Person; id: 0xd000000000040000 < x-coredata: //384642FD- C6B8-4F90-993B-755C44AB84A9 /Person /p1 >, data: < feil >) ")
For å få tilgang til attributtene til posten, gjør vi bruk av nøkkel-verdi koding som vi gjorde tidligere. Det er viktig å bli kjent med nøkkelverdien koding hvis du har tenkt å jobbe med kjernedata
if (result.count > 0) {NSManagedObject * person = (NSManagedObject *) [resultat objectAtIndex: 0].; NSLog (@ "1 -% @", person); NSLog (@ "% @% @", [person valueForKey: @ "første"], [person valueForKey: @ "siste"]); NSLog (@ "2 -% @", person);}
Du lurer kanskje på hvorfor jeg logger personen objekt før og etter å ha logget inn personens navn. Dette er faktisk en av de viktigste lærdommene fra denne artikkelen. Ta ser på resultatet under
kjernedata [1659: 613] 1 - < NSManagedObject. 0x109382980 > (enhet: Person; id: 0xd000000000040000 < x-coredata: //384642FD-C6B8-4F90-993B-755C44AB84A9 /Person /p1 >, data: < feil >) Kjerne data [1659: 613] Bart JacobsCore data [1659 : 613] 2 - < NSManagedObject: 0x109382980 > (enhet: Person; id: 0xd000000000040000 < x-coredata: //384642FD-C6B8-4F90-993B-755C44AB84A9 /Person /p1 >, data: {adresser = "< forholdet feil: 0x109380b20 adresser '>"; alder = 44; første = Bart, siste = Jacobs;})
Den første gangen vi logger personen objektet til konsollen, ser vi data: < feil >. Den andre gangen, men inneholder data innholdet i objektets attributter og relasjoner. Hvorfor det? Dette har alt å gjøre med forkastninger
, et nøkkelbegrep av grunndata.
5. Forkastninger
Konseptet som ligger til grunn forkastninger er ikke unikt for kjernedata. Hvis du noen gang har jobbet med Aktiv Record
i Ruby on Rails, deretter følgende vil sikkert ringe en bjelle. Konseptet er ikke identiske, men lignende fra en utviklers perspektiv.
kjernedata forsøker å beholde sin minne fotavtrykk så lavt som mulig, og en av de strategier som benyttes for å oppnå dette er forkastninger
. Når vi hentet postene for Person
enhet for et øyeblikk siden, Core data henrettet hente forespørsel, men det gjorde ikke fullt initial de administrerte gjenstander som representerer de hentet postene.
Hva vi fikk Ryggen er en feil, en plassholder objekt som representerer posten. Hensikten er av typen NSManagedObject og vi kan behandle det som sådan. Ved å ikke fullt initialisering posten, holder kjernedata minnet fotavtrykk lav. Det er ikke en betydelig minne sparing i vårt eksempel, men tenk hva som ville skje hvis vi hentet dusinvis, hundrevis eller kanskje tusenvis av poster.
Feil er generelt ikke noe du trenger å bekymre seg for. I det øyeblikket du tilgang til et attributt eller forholdet mellom en administrert objekt, er feilen sparken
, noe som betyr at grunndata endrer skyld inn en realisert klarte objekt. Du kan se dette i vårt eksempel, og det er også grunnen til at den andre logg uttalelse av personen objektet ikke skriver ut en feil til konsollen.
Feilende er noe som turer opp mange nykommere og jeg derfor ønsker å sørg for at du forstår det grunnleggende om dette konseptet. Vi vil lære mer om forkastninger senere i denne serien.
6. Oppdatering Records
Oppdatering poster er like enkelt som å lage en ny rekord. Du hente posten, endre et attributt eller forhold, og lagre klarte objektet sammenheng. Ideen er den samme som når du oppretter en post. Fordi klarte objekt, posten, er knyttet til en administrert objekt sammenheng, er sistnevnte klar over eventuelle endringer, innsettinger og oppdateringer. Når klarte objektet sammenheng lagres, alt er overført til kompet butikken ved kjernedata.
Ta en titt på følgende kode blokken der vi oppdaterer posten vi hentet ved å endre personens alder og lagre endringene .
NSManagedObject * person = (NSManagedObject *) [resultat objectAtIndex: 0] [person SetValue: @ 30 Forkey: @ "age"];! NSError * saveError = null; if ([person.managedObjectContext spare : & saveError]) {NSLog (@ ". Kan ikke lagre klarte objekt sammenheng"); NSLog (@ "% @,% @", saveError, saveError.localizedDescription);}
Du kan bekrefte at oppdateringen var vellykket ved å ta en titt på SQLite butikken som vi gjorde tidligere
7. . Slette Records
Slette en rekord følger samme mønster som å opprette og oppdatere poster. Vi fortelle klarte objekt sammenheng at en rekord må slettes fra den vedvarende butikken ved å kalle deleteObject. Og bestått klarte objektet som må slettes
I vårt prosjekt, sletter personen objektet vi hentet tidligere av den sendes til den klarte objektet sammenheng er deleteObject: metode. Legg merke til at slettingen ikke er forpliktet til å støtte butikken før vi kaller spare: på greid objektet sammenheng
NSManagedObject * person = (NSManagedObject *) [resultat objectAtIndex: 0] [self.managedObjectContext deleteObject: person].; NSError * deleteError = null; if ([person.managedObjectContext sparer: & deleteError]) {NSLog (@ ". Kan ikke lagre klarte objekt sammenheng"); NSLog (@ "% @,% @", deleteError, deleteError.localizedDescription);}
Konklusjon
I denne opplæringen, har vi dekket mye mer enn bare å skape, henting, oppdatering og sletting poster. Vi har rørt på noen viktige begreper som kjernedata er avhengig, for eksempel forkastninger og datamodell kompatibilitet.
I neste utgaven av denne serien, vil du lære hvordan du kan opprette og oppdatere relasjoner, og vi ta en grundig titt på NSFetchRequest klassen. Vi vil også begynne å bruke NSPredicate og NSSortDescriptor å gjøre våre hente forespørsler fleksibel, dynamisk og kraftfull.