Kjernen data fra Scratch: Concurrency

Core data fra Scratch: Samtidighet

30
Del
6
Del

Dette Cyber ​​mandag Envato Tuts + Kursene 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:. Subclassing NSManagedObjectiOS 8: kjernedata og Batch oppdateringer

Hvis du utvikler en liten eller enkelt program, så har du sannsynligvis ikke ser fordelen av å kjøre grunndata operasjoner i bakgrunnen. Men hva ville skje hvis du importert hundrevis eller tusenvis av poster på hovedtråden under den første lanseringen av søknaden din? Konsekvensene kan bli dramatiske. For eksempel kan søknaden bli drept av Apples vaktbikkje for å ta for lang tid å starte.

I denne artikkelen tar vi en titt på farene ved bruk av grunndata på flere tråder, og vi utforske flere løsninger å takle problemet.

1. Tråd Safety

Når du arbeider med kjernedata, er det viktig å alltid huske at kjernedata ikke er tråd trygt. Kjerne data regner med å bli kjørt på en enkelt tråd. Dette betyr ikke at alle kjernedata operasjonen må utføres på hovedtråden, som er sant for UIKit, men det betyr ikke at du trenger å være klar over hvilke operasjoner utføres på hvilke tråder. Det betyr også at du må være forsiktig med hvordan endringer fra en tråd blir overført til andre tråder.

Arbeide med kjernedata på flere tråder er faktisk veldig enkelt fra et teoretisk synspunkt. NSManagedObject, NSManagedObjectContext, og NSPersistentStoreCoordinator ikke tråden trygt, og forekomster av disse klassene skal kun være tilgjengelige fra tråden de ble opprettet på. Som du kan forestille deg, blir dette litt mer komplisert i praksis.

NSManagedObject

Vi vet allerede at NSManagedObject klassen ikke er tråd trygt, men hvordan får du tilgang til et rekord fra forskjellige tråder? En NSManagedObject eksempel har en objectID metode som returnerer en forekomst av NSManagedObjectID klassen. Den NSManagedObjectID klassen er tråd trygt og en forekomst inneholder all informasjon en administrert objekt sammenheng behov for å hente den tilsvarende klarte objektet
//Object ID Managed ObjectNSManagedObjectID * objectID = [managedObject objectID];.

I det følgende kodebiten , spør vi en administrert objekt kontekst for den administrerte objekt som stemmer overens med objectID. De objectWithID: og existingObjectWithID: feil: metoder returnere en lokal versjon-lokale til gjeldende tråd-av den tilsvarende klarte objektet
//Fetch Managed ObjectNSManagedObject * managedObject = [self.managedObjectContext objectWithID: objectID];. //OR //Fetch Managed ObjectNSError * error = null; NSManagedObject * managedObject = [self.managedObjectContext existingObjectWithID: objectID error: & error]; if (feil) {NSLog (@ ". Klarte ikke å hente klarte objekt med objekt-ID,% @", objectID); NSLog (@ "% @,% @", feil, error.localizedDescription);}

Den grunnleggende regel å huske er å ikke passere NSManagedObject eksempel fra en tråd til en annen. I stedet passere klarte objektets objectID og spør tråden er administrert objekt kontekst for en lokal versjon av forvaltet objektet.

NSManagedObjectContext

Fordi NSManagedObjectContext klassen ikke er tråd trygt, vi kunne skape en administrert objekt kontekst for hver tråd som samhandler med kjernedata. Denne strategien er ofte referert til som tråden sperring
.

En vanlig metode er å lagre klarte objekt sammenheng i tråden ordliste, en ordbok for å lagre tråden spesifikke data. Ta en titt på følgende eksempel for å se hvordan dette fungerer i praksis
//Legg Object til Træ DictionaryNSThread * currentThread = [NSThread currentThread] [[currentThread threadDictionary] setObject: managedObjectContext Forkey: @ "managedObjectContext"];. < h3> NSPersistentStoreCoordinator

Hva om den vedvarende butikken koordinator? Trenger du å opprette en egen vedvarende butikken koordinator for hver tråd. Selv om dette er mulig, og en av strategiene Apple som brukes til å anbefale, er det ikke nødvendig.

NSPersistentStoreCoordinator klassen er designet for å støtte flere administrerte objekt sammenhenger, selv om de klarte objekt sammenhenger ble opprettet på forskjellige tråder. Fordi NSManagedObjectContext klasse låser vedvarende butikken koordinator ved tilgang til det, er det mulig for flere klarte objekt sammenhenger å bruke samme vedvarende butikken koordinator selv om de klarte objekt sammenhenger leve på forskjellige tråder. Dette gjør en flertrådet kjerne Data oppsett mye mer håndterlig og mindre komplisert.

2. Samtidighet Strategier

Så langt har vi lært at du trenger flere klarte objekt sammenhenger hvis du utfører kjernedata operasjoner på flere tråder. Det forbeholdet er imidlertid at administrerte objekt sammenhenger er uvitende om hverandres eksistens. Endringer som gjøres i en administrert objekt i én klarte objekt sammenheng blir ikke automatisk overført til andre administrerte objekt sammenhenger. Hvordan løser vi dette problemet?

Det er to populære strategier som Apple anbefaler, varslinger og foreldre-barn-administrerte objekt sammenhenger. La oss se på hver strategi og undersøke sine fordeler og ulemper.

Scenariet vi tar som et eksempel er en NSOperation underklasse som utfører arbeid i bakgrunnen og åpner Kjerne data om bruken bakgrunn tråden. Dette eksemplet viser forskjellene og fordelene ved hver strategi

Strategi 1:. Påminnelser

Tidligere i denne serien, jeg introduserte deg til NSFetchedResultsController klasse og du lært at en administrert objekt kontekst innlegg tre typer varslinger:

NSManagedObjectContextObjectsDidChangeNotification: denne meldingen er lagt ut når en av de administrerte objekter av den administrerte objektet sammenheng har endret

NSManagedObjectContextWillSaveNotification: denne meldingen er lagt ut før
forvaltet objektet sammenheng utfører en lagringen

NSManagedObjectContextDidSaveNotification: denne meldingen er lagt ut etter
klarte objektet sammenheng utfører en lagringen

Når en administrert objekt sammenheng lagrer sine endringer til et vedvarende butikken, via den vedvarende butikken koordinator, andre klarte objekt sammenhenger kan være lurt å vite om disse endringene. Dette er veldig enkelt å gjøre, og det er enda enklere å inkludere eller slå sammen endringene i en annen klarte objekt sammenheng. La oss snakke kode.

Vi skaper et ikke-samtidig drift som gjør noe arbeid i bakgrunnen og trenger tilgang til kjernedata. Overskriften vil ligne på det som vises nedenfor
#import < Foundation /Foundation.h > #import < CoreData /CoreData.h >interface TSPImportOperation. NSOperation @ eiendom (sterk, nonatomic) NSManagedObjectContext * mainManagedObjectContext; end

Operasjonen grensesnitt er veldig enkelt som det bare inneholder en eiendom for programmets hoved klarte objekt sammenheng. Det er flere grunner til å holde en referanse til programmets hoved klarte objekt sammenheng. Dette blir tydelig når vi inspisere gjennomføringen av TSPImportOperation klassen.

Vi erklærer først en privat eiendom, privateManagedObjectContext, av type NSManagedObjectContext. Dette er forvaltet objektet sammenheng at operasjonen vil bruke internt til å utføre kjernedata oppgaver
#import "TSPImportOperation.h"@interface TSPImportOperation ()property (sterk, nonatomic) NSManagedObjectContext * privateManagedObjectContext;.end

Fordi vi gjennomføre en ikke-samtidig NSOperation underklasse, må vi gjennomføre den viktigste metoden. Dette er hva det ser ut som Anmeldelser - (void) hoved {//Initial Managed Object Context self.privateManagedObjectContext = [[NSManagedObjectContext Alloc] init].; //Konfigurer Managed Object Kontekst [self.privateManagedObjectContext setPersistentStoreCoordinator: self.mainManagedObjectContext.persistentStoreCoordinator]; //Legg Observer NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver bolig: Velger:selector (managedObjectContextDidSave :) navn: NSManagedObjectContextDidSaveNotification objekt: self.privateManagedObjectContext]; //Gjøre noe arbeid //... if ([self.privateManagedObjectContext hasChanges]) {//Lagre endringer NSError * error = null; [self.privateManagedObjectContext spare: & error]; }}

Det er noen viktige detaljer som må avklares. Vi initialisere privat administrerte objektet kontekst og sette vedvarende butikken koordinator eiendom med mainManagedObjectContext objektet. Dette er helt greit, fordi vi ikke får tilgang til mainManagedObjectContext, vi bare spør om det for sin referanse til programmets vedvarende butikken koordinator. Vi bryter ikke tråden sperring regelen.

Det er viktig å initialisere private klarte objekt sammenheng i virksomhetens viktigste metoden, fordi denne metoden utføres på bakgrunn tråden der driften går. Kan vi ikke initialisere klarte objekt sammenheng i operasjonens init-metoden? Svaret er nei. Operasjonen er init metode drives på tråden der TSPImportOperation er initialisert, som er mest sannsynlig hovedtråden. Dette ville tap hensikten med en privat klarte objekt sammenheng.

I virksomhetens viktigste metoden, legger vi den TSPImportOperation eksempel som en observatør av eventuelle NSManagedObjectContextDidSaveNotification meldinger postet av private klarte objektet sammenheng. Anmeldelser

Vi deretter gjøre jobben operasjonen ble opprettet for, og lagre endringene av private klarte objektet sammenheng, noe som vil utløse en NSManagedObjectContextDidSaveNotification varsling. La oss ta en titt på hva som skjer i managedObjectContextDidSave: metode Anmeldelser #pragma mark - # pragma mark Notification håndterings- (void) managedObjectContextDidSave:. (NSNotification *) varsling {dispatch_async (dispatch_get_main_queue () ^ {[self.mainManagedObjectContext mergeChangesFromContextDidSaveNotification : varsel];});}

Som du kan se, er kort og enkel gjennomføring. Vi kaller mergeChangesFromContextDidSaveNotification: på hoved klarte objektet sammenheng, passerer i varslings objektet. Som jeg nevnte tidligere, inneholder meldingen endringene, innstikk, oppdateringer og sletter, av private klarte objektet sammenheng. Det er nøkkelen til å kalle denne metoden på tråden hoved klarte objektet sammenheng ble opprettet på, hovedtråden. Det er derfor vi sende dette kallet til hovedtråden.

Putting TSPImportOperation klassen å bruke er så enkelt som initialisering en forekomst, sette sin mainManagedObjectContext eiendom, og legge driften til en operasjon køen.
//Initial Import OperationTSPImportOperation * drift = [[TSPImportOperation Alloc] init]; //Konfigurer Import Operation [drift setMainManagedObjectContext: self.managedObjectContext]; //Legg til Operation Queue [self.operationQueue addOperation: operasjon];
Strategi 2: Parent /Barne Managed Object sammenhenger

Siden iOS 6, er det en enda bedre, mer elegant strategi. La oss se de TSPImportOperation klasse og innflytelse foreldre /barn administrerte objekt sammenhenger. Konseptet bak foreldre /barn klarte objekt sammenhenger er enkel, men kraftig. La meg forklare hvordan det fungerer.

Et barn klarte objekt sammenheng er avhengig av moder klarte objekt kontekst for å spare sine endringer til tilsvarende vedvarende butikken. Faktisk har et barn klarte objekt sammenheng har ikke tilgang til et vedvarende butikken koordinator. Når et barn klarte objekt sammenheng er lagret, blir endringene den inneholder presset til den overordnede klarte objektet sammenheng. Det er ikke nødvendig å bruke varslinger til å slå sammen endringene i hoved eller forelder klarte objekt sammenheng manuelt.

En annen fordel er ytelsen. Fordi barnet klarte objektet sammenheng ikke har tilgang til den vedvarende butikken koordinator, blir endringene ikke presset til sistnevnte når barnet klarte objektet sammenheng reddes. I stedet blir de endringene skjøvet til foreldre klarte objektet sammenheng, skitner den. Endringene blir ikke automatisk overført til den vedvarende butikken koordinator.

Managed objekt sammenhenger kan nestes. Et barn klarte objekt sammenheng kan ha et barn klarte objekt sammenheng med sin egen. De samme reglene gjelder. Men det er viktig å huske på at de endringene som blir presset opp til den overordnede klarte objektet sammenheng ikke er presset ned til eventuelle andre barn klarte objekt sammenhenger. Hvis barnet En presset sine endringer til sin overordnede, så barnet B er uvitende om disse endringene.

Opprette et barn klarte objekt sammenheng er litt forskjellig fra det vi har sett så langt. Et barn klarte objekt sammenheng bruker en annen initializer, initWithConcurrencyType :. Den samtidighet typen initializer aksepterer definerer klarte objektet sammenheng er threading modell. La oss se på hver samtidighet typen

NSMainQueueConcurrencyType. Den klarte objektet sammenheng er kun tilgjengelig fra hovedtråden. Et unntak hvis du prøver å få tilgang til den fra en annen tråd

NSPrivateQueueConcurrencyType. Når du oppretter en administrert objekt sammenheng med en samtidighet type NSPrivateQueueConcurrencyType blir forvaltet objektet sammenheng knyttet til en privat kø, og det kan bare nås fra at private køen

NSConfinementConcurrencyType. Dette er samtidighet typen som samsvarer med tråden sperring
konsept vi utforsket tidligere. Hvis du oppretter en administrert objekt sammenheng med init-metoden, den samtidighet type som klarte objektet sammenheng er NSConfinementConcurrencyType.

Det er to viktige metoder som ble lagt til Core data rammeverket når Apple introduserte foreldre /barne klarte objekt sammenhenger, performBlock: og performBlockAndWait :. Begge metodene vil gjøre livet mye enklere. Når du ringer performBlock: på en administrert objekt kontekst og bestå i en blokk med kode for å utføre, gjør kjernedata for at blokken blir utført på riktig tråd. I tilfelle av NSPrivateQueueConcurrencyType samtidighet typen, betyr dette at blokken blir utført på den private kø av den administrerte forbindelse objektet

Forskjellen mellom performBlock:. Og performBlockAndWait: er enkel. Den performBlock: metode blokkerer ikke gjeldende tråd. Det aksepterer blokken, tidsplaner det til utførelse på riktig kø, og fortsetter med gjennomføringen av den neste setningen

performBlockAndWait. Metode, men blokkerer. Tråden som performBlockAndWait: kalles venter på blokken som sendes til metoden for å fullføre før du utfører neste setning. Fordelen er at nøstede samtaler til performBlockAndWait:. Utføres for

For å avslutte denne artikkelen, vil jeg gjerne refactor den TSPImportOperation klassen til å dra nytte av foreldre /barn administrerte objekt sammenhenger. Du vil raskt merke at det forenkler TSPImportOperation underklasse.

Den header forblir uendret, men den viktigste metoden endrer ganske mye. Ta en titt på sin oppdaterte gjennomføring under Anmeldelser - (void) hoved {//Initial Managed Object Context self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType].; //Konfigurer Managed Object Kontekst [self.privateManagedObjectContext setParentContext: self.mainManagedObjectContext]; //Gjøre noe arbeid //... if ([self.privateManagedObjectContext hasChanges]) {//Lagre endringer NSError * error = null; [self.privateManagedObjectContext spare: & error]; }}

Det var det. Hoved klarte objektet sammenheng er forelder til privat administrerte objektet sammenheng. Merk at vi ikke stiller persistentStoreCoordinator tilhører den private klarte objektet sammenheng, og vi legger ikke til operasjonen som observatør for NSManagedObjectContextDidSaveNotification varslinger. Når den private klarte objektet sammenheng er lagret, blir endringene automatisk skyves til den overordnede klarte objekt sammenheng. Kjernedata sikrer at dette skjer på riktig tråd. Det er opp til hoved klarte objektet sammenheng, den overordnede klarte objektet sammenheng, for å presse endringene til den vedvarende butikken koordinator.

Konklusjon

Samtidighet er ikke lett å forstå eller implementere, men det er naivt å tro at du aldri kommer over en situasjon hvor du må utføre kjernedata operasjoner på en bakgrunnstråd.

I de neste to artiklene, vil jeg fortelle deg om iOS 8 og Kjernen data
. Apple introduserte en rekke nye APIer i iOS 8 og OS X 10.10, inkludert batch oppdatering og asynkron henting.