Kjernen data fra Scratch: subclassing NSManagedObject

Core data fra Scratch: subclassing NSManagedObject
20
Del
9
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:. MigrationsCore data fra Scratch: Samtidighet
1. Innledning

Tidligere i denne serien, skapte vi gjort, et enkelt program for å lære mer om NSFetchedResultsController klassen. I dette prosjektet har vi brukt nøkkelverdien koding (KVC) og nøkkelverdi observere (KVO) å opprette og oppdatere poster. Dette fungerer fint, men fra det øyeblikket prosjektet har noen form for kompleksitet, vil du raskt får problemer. Ikke bare er KVC syntaks ordrik, valueForKey: og SetValue: Forkey :, det kan også introdusere feil som er et resultat av skrivefeil. Følgende kodebiten er et godt eksempel på det siste problemet
[rekord SetValue: [NSDate dato] Forkey: @ "createdat"] [rekord SetValue: [NSDate dato] Forkey: @ "CreatedAt"];. [Rekord SetValue: [NSDate dato] Forkey: @ "createdAt"] [rekord SetValue: [NSDate dato] Forkey: @ "CREATEDAT"];

Hver uttalelse i ovennevnte kodebiten returnerer et annet resultat. Faktisk vil enhver uttalelse resultere i et unntak bortsett fra tredje setningen, som bruker riktig nøkkel som spesifisert i prosjektets datamodellen.

Ovennevnte problem er lett løses ved å bruke string konstanter, men det er ikke Poenget jeg prøver å gjøre. Nøkkelverdi koding er flott, men det er ordrik og vanskelig å lese hvis du er vant til Objective-C er dot syntaks. For å gjøre arbeidet med NSManagedObject tilfeller lettere, er det bedre å lage en NSManagedObject underklasse for hver enhet av datamodellen og det er hva du vil lære i denne artikkelen.

2. Subclassing NSManagedObject

For å spare litt tid, vi kommer til å besøke Ferdig, programmet vi laget tidligere i denne serien. Last den ned fra GitHub og åpne den i Xcode.

Opprette en NSManagedObject underklasse er veldig enkelt. Selv om det er mulig å lage en NSManagedObject underklasse manuelt for en enhet, er det lettere å la Xcode gjøre jobben for deg.

Åpne prosjektets datamodellen, Done.xcdatamodeld
, og velg < b> TSPItem
enhet. Velg New > Fil ...
fra Xcode Fil
menyen velger du NSManagedObject underklasse
mal fra kjernedata
delen, og klikk Neste Anmeldelser .

Merk av i boksen av riktig datamodellen, Ferdig
, fra listen over datamodeller og klikk Neste
.

I neste trinn , blir du bedt om å velge de enhetene som du ønsker å opprette en NSManagedObject underklasse. Merk av i boksen for TSPItem
enhet og klikk Neste
.

Velg et sted å lagre klassen filene i NSManagedObject underklasse og sørg for at Bruk skalar eiendommer for primitive datatyper
er avmerket. Jeg skal forklare betydningen av dette alternativet i en liten stund. Klikk Opprett
å opprette NSManagedObject underklasse for TSPItem
enhet.

3. NSManagedObject Anatomy

Interface

Naviger til filer Xcode opprettet for deg og ta en titt på innholdet. Topptekstfilen, TSPItem.h
, bør ligne på det som er vist nedenfor
#import < Foundation /Foundation.h > #import < CoreData /CoreData.h >interface TSPItem.: NSManagedObject @ eiendom (nonatomic, beholde) NSDate * createdAt;property (nonatomic, beholde) NSNumber * gjort;property (nonatomic, beholde) NSString * navn,end

På toppen, bør du se importregnskapet for Foundation Hotell og Kjernedata
rammer. Den NSManagedObject underklasse inneholder tre eiendommer, tilsvarende med attributtene til TSPItem
enhet av datamodellen. Det er noen forskjeller skjønt.

De typer navn og createdAt egenskaper, NSString, er ikke overraskende. Typen gjort eiendom, men er mindre opplagt. Selv om vi erklærte type gjort
attributt som en boolsk i datamodellen, er det gjort egenskap av typen NSNumber. Grunnen til dette er enkel. Når vi skapte NSManagedObject underklasse en liten stund siden, dro vi av i boksen merket Bruk skalar egenskaper for primitive datatyper
ukontrollert. Hvis vi hadde sjekket at boksen, ville den gjort eiendommen være av typen BOOL.

Selv om det er opp til deg å bestemme om du ønsker å jobbe med gjenstander eller primitive datatyper, det er en grunn til at avmerkingsboksen er avmerket som standard. Du vil raskt finne ut at det er mindre praktisk å bruke primitive datatyper for egenskapene til en NSManagedObject underklasse. Det kan se bra ut på papiret, men det blir en problemfri når du for eksempel ønsker å lagre primitive datatyper i Objective-C samlinger, som bare godtar stedene.

Implementering

Gjennomføringen av TSPItem klassen er enda kortere og den bruker en kompilator direktiv som kan være nye for deg
#import "TSPItem.h"@implementation TSPItem @ dynamisk createdAt;.dynamic gjort;dynamic navn;end

Som brukerdynamic direktivet kompilatoren vet at accessors (kundeskaffere og settere) for eiendommene deklarert i klassen grensesnitt vil bli generert under kjøring. Kompilatoren tar vårt ord for det og sender ingen advarsler.

Hvis du kommenterer utdynamic uttalelser, genererer kompilatoren tre advarsler, forteller oss at de accessors for de tre eiendommene mangler.

4. Oppdatere Prosjekt

Med TSPItem klassen klar til bruk, er det på tide å oppdatere prosjektet ved å erstatte eventuelle forekomster av valueForKey: og SetValue: Forkey :.

TSPViewController

Åpne gjennomføringen fil av TSPViewController klassen og begynne med å legge en import regnskapet for TSPItem klassen på toppen
#import "TSPItem.h"

Gå til configureCell. atIndexPath: og oppdatere gjennomføringen som vist nedenfor.
- (void) configureCell: (TSPToDoCell *) celle atIndexPath: (NSIndexPath *) indexPath {//Fetch Record TSPItem * record = [self.fetchedResultsController objectAtIndexPath: indexPath]; //Update Cell [cell.nameLabel setText: record.name]; [cell.doneButton setSelected: [record.done boolValue]]; [celle setDidTapButtonBlock: ^ {BOOL isdone = [record.done boolValue]; //Update Record record.done = @ (isdone!); }];}

Vi har gjort fem endringer. Vi først endret type posten variable til TSPItem. Den objectAtIndexPath: metoden i NSFetchedResultsController klassen returnerer en forekomst av NSManagedObject klassen. Dette er fortsatt sant siden den TSPItem klassen er en NSManagedObject underklasse

Vi erstatter også valueForKey. Samtaler. I stedet bruker vi egenskapene til posten objekt, navn og gjort. Takk til Objective-C er dot syntaks, er resultatet svært leselig. Legg merke til at vi kaller boolValue på plata er gjort eiendom. Husk at det gjøres Eiendommen er av typen NSNumber, det er derfor vi trenger å ringe boolValue på den for å få den faktiske boolsk verdi. . Å sette verdien av isdone variabel, gjentar vi dette trinnet

For å oppdatere posten, vi ikke lenger kalle SetValue: Forkey :. I stedet bruker vi prikken syntaksen til å sette rekord gjøres eiendom. Jeg er sikker på at du enig i at dette er mye mer elegant enn å bruke rett tast verdi koding.

Husk at KVC og KVO forbli en integrert del av kjernedata. Kjernen data bruker valueForKey: og SetValue: Forkey: under panseret for å få jobben gjort

TSPAddToDoViewController

Vi må også gjøre noen endringer i TSPAddToDoViewController klassen.. Begynn med å legge en import regnskapet for TSPItem klassen. I spare: metoden, må vi først å oppdatere initialisering av NSManagedObject eksempel. I stedet for initialisering en NSManagedObject eksempel, skaper vi en TSPItem eksempel
//Initial RecordTSPItem * record = [[TSPItem alloc] initWithEntity: enhet insertIntoManagedObjectContext: self.managedObjectContext];.

For å fylle opp, bruker vi prikken Syntaksen i stedet for SetValue. Forkey metode som vist nedenfor
//Befolke Recordrecord.name = navn; record.createdAt = [NSDate dato];
TSPUpdateToDoViewController

Den siste klassen vi trenger å oppdatere er TSPUpdateToDoViewController klasse. La oss starte med klassen grensesnitt. Åpne TSPUpdateToDoViewController.h Hotell og oppdatere innholdet som vist nedenfor. Vi legger en fremtids klasse erklæring for TSPItem klasse på toppen og endre typen posten eiendom til TSPItem
#import. ≪ UIKit /UIKit.h > #import < CoreData /CoreData.h >class TSPItem;interface TSPUpdateToDoViewController: UIViewController @ eiendom (svak, nonatomic) IBOutlet UITextField * Textfield,property (sterk, nonatomic) NSManagedObjectContext * managedObjectContext;property (sterk, nonatomic) TSPItem * record;end

Denne endringen vil føre til en advarsel i TSPViewController klassen. Å se hva som er galt, åpne gjennomføringen fil av TSPViewController klasse og naviger til prepareForSegue: avsenderen. Metode

Vi ber hentet resultater kontrolleren for posten på den valgte indeksen banen, self.selection. Typen posten variabelen er NSManagedObject, men TSPUpdateToDoViewController klassen forventer en TSPItem eksempel. Løsningen er svært enkel som du kan se nedenfor
if (self.selection) {//Fetch Record TSPItem * record = [self.fetchedResultsController objectAtIndexPath: self.selection].; if (rekord) {[vc setRecord: record]; } //Tilbake Selection [selv setSelection: null];}

Leder tilbake til TSPUpdateToDoViewController klassen og legge en import regnskapet for TSPItem klassen på toppen av gjennomføringen fil. Oppdatere viewDidLoad metoden som vist nedenfor Anmeldelser - (void) viewDidLoad {[super viewDidLoad].; if (self.record) {//Update Text Feltet [self.textField setText: self.record.name]; }}

Vi trenger også å oppdatere spare: metode, der vi erstatte SetValue: Forkey: med prikken syntaks
//Befolke Recordself.record.name = navn;

Bygg prosjektet og kjøre. programmet i iOS Simulator for å se om alt er fortsatt fungerer som forventet.

5. Relasjoner

Den nåværende datamodellen inneholder ingen relasjoner, men la oss legge til noen for å se hvordan en NSManagedObject underklasse med relasjoner ser ut. Som vi har sett i den forrige artikkelen i denne serien, må vi først å lage en ny versjon av datamodellen. Velg datamodellen i Prosjekt Navigator Hotell og velg Legg Model versjon ...
fra Editor
menyen. Sette Version navn
til Ferdig 2 Hotell og basere modellen på dagens datamodell, Ferdig
. Klikk Fullfør
å opprette den nye datamodellen versjon.

Åpne Ferdig 2.xcdatamodel
, opprette en ny enhet som heter TSPUser
, og legge til en attributt navn
av type String
. Legg et forhold elementer Hotell og satt målet til TSPItem
. La den inverse forholdet tomt for nå. Med elementer
forholdet valgt, åpner du Data Model Inspektør
på høyre og sette forholdet typen til Slik Mange
. En bruker kan ha mer enn ett element knyttet til den.

Velg TSPItem
enhet, skape et forhold bruker
, og angi dette som reisemål til TSPUser
. Sett inverse forholdet til elementer
. Dette vil automatisk sette den inverse forholdet mellom de elementer
forholdet til TSPUser
enhet.

Før skaper vi NSManagedObject underklasser for begge enhetene, må vi fortelle data modell som datamodell versjon den skal bruke. Velg Done.xcdatamodeld
, åpne File Inspektør
på høyre side, og sette dagens modell versjon til Ferdig 2
. Når du gjør dette, dobbeltsjekke at du har valgt Done.xcdatamodeld
, ikke Done.xcdatamodel
.

Fjern TSPItem.h Anmeldelser og TSPItem.m
fra prosjektet. Velg New > Fil ...
fra Fil
menyen og velg NSManagedObject underklasse
mal fra Kjerne data
delen. Fra listen over datamodeller, velg Ferdig 2
.

Velg begge enhetene fra listen over enheter. Fordi vi har endret TSPItem
enhet, må vi fornye den tilsvarende NSManagedObject underklasse.

TSPItem

La oss først ta en titt på endringene i den nylig genererte TSPItem klasse. . Som du kan se nedenfor, inneholder header filen én ekstra eiendom, bruker av type TSPUser
For å tilfreds kompilatoren, Xcode også lagt til en videre klasse erklæring på toppen
#import. ≪ Foundation /Foundation.h > #import < CoreData /CoreData.h >class TSPUser;interface TSPItem: NSManagedObject @ eiendom (nonatomic, beholde) NSDate * createdAt;property (nonatomic, beholde) NSNumber * gjort;property (nonatomic, beholde) NSString * navn,property (nonatomic, beholde) TSPUser * bruker;.end

Gjennomføringen filen reflekterer tillegg til brukeren eiendom
#import "TSPItem.h" #import "TSPUser.h" implementation TSPItem @ dynamisk createdAt;dynamic gjort,dynamic navn,dynamic bruker;end

Dette er hva en Til En
forhold ser ut i en NSManagedObject underklasse. Xcode er smart nok til å antyde at den type brukeren eiendommen er TSPUser, den NSManagedObject underklasse vi skapt et øyeblikk siden.

TSPUser

TSPUser klassen er litt mer komplisert. Ta en titt på den klassen grensesnitt. Det første du vil legge merke til er at den type elementer eiendommen er NSSet. Dette bør ikke være en overraskelse, fordi vi allerede visste at grunndata bruker NSSet klasse for lagring av medlemmene i en Slik Mange

forhold
#import. ≪ Foundation /Foundation.h > #import < CoreData /CoreData.h >class TSPItem;interface TSPUser: NSManagedObject @ eiendom (nonatomic, beholde) NSString * navn,property (nonatomic, beholde) NSSet * elementer;end

Overskriften fil av TSPUser klassen inneholder også en klasse utvidelse som omfatter fire dagligvare metoder. Dette er en annen fordel med å bruke en NSManagedObject underklasse og la Xcode generere det for oss. I stedet for direkte manipulere elementer eiendommen, kan vi legge til og fjerne TSPItem tilfeller ved hjelp av disse praktiske metoder
interface TSPUser (CoreDataGeneratedAccessors) - (void) addItemsObject: (TSPItem *) verdi; - (void) removeItemsObject:. (TSPItem * ) verdi; - (void) addItems: (NSSet *) verdier, - (void) removeItems: (NSSet *) verdier;end

La meg vise deg hvorfor disse praktiske metodene er så nyttig. La oss se hva det tar å legge en TSPItem objekt til en TSPUser eksempel med og uten disse praktiske metoder Twitter /** * Med Convenience metode * /[user addItemsObject: element];. /** * Uten Convenience metode * /NSMutableSet * varer = [[User elementer] mutableCopy]; [elementer addObject: element] [bruker setItems: elementer];

Under panseret, men bruker kjernedata nøkkelverdi koding for å legge til TSPItem forekomsten til elementer eiendom . brukerobjektet
NSMutableSet * elementer = [user mutableSetValueForKey: @ "elementer"]; [elementer addObject: punkt];
6. Migrations

Hvis du bygger prosjektet og kjøre programmet på iOS Simulator, vil du legge merke til at programmet krasjer. Utgangen i Xcode konsoll forteller at datamodellen brukes til å åpne den vedvarende butikken er ikke kompatibel med den som ble brukt til å opprette det. Grunnen til dette problem er forklart i detalj i den tidligere artikkelen i denne serien. Hvis du ønsker å lære mer om vandringer og hvordan du trygt endre datamodellen, så foreslår jeg at du leser den artikkelen.

Konklusjon

subclassing NSManagedObject er veldig vanlig når man jobber med kjernedata. Ikke bare legge den typen sikkerhet, også gjør det å jobbe med relasjoner mye enklere.

I neste utgaven av denne serien tar vi en nærmere titt på kjernedata og samtidighet. Samtidighet er et vanskelig begrep i nesten alle programmeringsspråk, men vet hvilke fallgruver å unngå, gjør det mye mindre skummelt.