Kjernen data og Swift: Concurrency

Core Data- og Swift: Samtidighet

12
Del
4
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 og Swift.Core Data- og Swift. Subclassing NSManagedObject

Hvis du utvikler en liten eller enkelt program, så har du sannsynligvis ikke ser fordelen av å kjøre Kjernedataoperasjoner 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 bevisst 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. 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 ikke tråden trygt, men hvordan kan du få tilgang til en oversikt fra annen tråder? En NSManagedObject eksempel har en objectID egenskap som returnerer en forekomst av NSManagedObjectID klassen. Den NSManagedObjectID klassen er
tråden trygg og en forekomst av denne klassen inneholder all informasjon som en administrert objekt sammenheng behov for å hente den tilsvarende klarte objektet.
//Object ID Managed Objectlet objectID = managedObject.objectID

I det følgende kodebiten, ber vi om en administrert objekt kontekst for den administrerte objekt som stemmer overens med objectID. Den objectWithID (_ :) og existingObjectWithID (_ :) metoder returnere en lokal versjon-lokale til gjeldende tråd-av den tilsvarende klarte objektet.
//Fetch Managed Objectlet managedObject = managedObjectContext.objectWithID (objectID) //Ordo { la managedObject = prøve managedObjectContext.existingObjectWithID (objectID)} catch {la fetchError = feil som NSError print ("\\ (fetchError), \\ (fetchError.userInfo)")}

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 posten.

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 data som er spesifikke for tråden. Ta en titt på følgende eksempel for å se hvordan dette fungerer i praksis
//Legg Object til Træ Dictionarylet currentThread = NSThread.currentThread () currentThread.threadDictionary.setObject (managedObjectContext, Forkey: "managedObjectContext").

Ikke altfor lenge siden, anbefales Apple denne tilnærmingen. Selv om det fungerer fint, det er en annen og bedre alternativ som Apple anbefaler nå til dags. Vi skal se på dette alternativet i en liten stund.

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, dette er 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
klarte 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. Dette er hva gjennomføringen av NSOperation underklasse kan se ut
import UIKitimport CoreDataclass Operation:. NSOperation {la mainManagedObjectContext: NSManagedObjectContext Var privateManagedObjectContext: NSManagedObjectContext! init (managedObjectContext: NSManagedObjectContext) {mainManagedObjectContext = managedObjectContext super.init ()} overstyring func main () {//Initial Managed Object Context privateManagedObjectContext = NSManagedObjectContext (concurrencyType: .PrivateQueueConcurrencyType) //Konfigurer Managed Object Context privateManagedObjectContext.persistentStoreCoordinator = mainManagedObjectContext.persistentStoreCoordinator //Legg Observer la notificationCenter = NSNotificationCenter.defaultCenter () notificationCenter.addObserver (selv, velger: "managedObjectContextDidSave:", navn: NSManagedObjectContextDidSaveNotification, objekt: privateManagedObjectContext) //gjøre noe arbeid //... hvis privateManagedObjectContext.hasChanges {do { prøv privateManagedObjectContext.save ()} catch {//Feilhåndtering //...}}}}

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 operasjonens main () metode, fordi denne metoden utføres på bakgrunn tråden der driften går. Kan vi ikke initialisere klarte objekt sammenheng i operasjonens init (managedObjectContext :) metoden? Svaret er nei. Virksomhetens init (managedObjectContext :) metoden kjøres på tråden der Operation eksempel er initialisert, som er mest sannsynlig hovedtråden. Dette ville tap hensikten med en privat klarte objekt sammenheng.

I operasjonens main () metoden, legger vi den Operation eksempel som en observatør av eventuelle NSManagedObjectContextDidSaveNotification meldinger postet av private klarte objektet sammenheng.
< p> Vi gjør 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
//MARK: - //MARK: Melding Handlingfunc managedObjectContextDidSave (varsling. NSNotification) {dispatch_async (dispatch_get_main_queue ()) {() - > Ugyldig i self.mainManagedObjectContext.mergeChangesFromContextDidSaveNotification (varsling)}}

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, inserts, oppdateringer og sletter, av den administrerte objektet kontekst som postet meldingen.

Det er nøkkelen til å kalle denne metoden på tråden hoved klarte objektet sammenheng var opprettet på, hovedtråden. Det er derfor vi sende dette kallet til køen av hovedtråden. For å gjøre dette enklere og mer transparent, kan du bruke performBlock (_ :) eller performBlockAndWait (_ :) å sikre sammenslåing endringene finner sted på køen av den administrerte objektet sammenheng. Vi skal snakke mer om disse metodene senere i denne artikkelen
//MARK: - //MARK: Melding Handlingfunc managedObjectContextDidSave (varsling: NSNotification) {mainManagedObjectContext.performBlock {() - >. Void i self.mainManagedObjectContext.mergeChangesFromContextDidSaveNotification (varsling)}}

Putting Operation klassen å bruke er så enkelt som initialisering en forekomst, passerer en administrert objekt sammenheng, og legge driften til en operasjon køen.
//Initial Import Operationlet drift = Operation (managedObjectContext: managedObjectContext) //Legg til Operation QueueoperationQueue.addOperation (drift)
Strategi 2: foreldre /barn Managed Object sammenhenger

Siden iOS 6, er det en enda bedre, mer elegant strategi. La oss se de Operation 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, noe som gjør det skitne. 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 presser sine endringer til sin overordnede, så barnet B er uvitende om disse endringene.

Opprette et barn klarte objekt sammenheng er bare litt forskjellig fra hva vi har sett så langt. Vi initial et barn klarte objekt sammenheng ved å påberope init (concurrencyType :). Den samtidighet typen initializer aksepterer definerer klarte objektet sammenheng er threading modell. La oss se på hver samtidighet typen

MainQueueConcurrencyType. 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

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

ConfinementConcurrencyType. Dette er samtidighet typen som samsvarer med tråden sperring
konsept vi utforsket tidligere. Hvis du oppretter en administrert objekt sammenheng bruker init (), den samtidighet type som klarte objektet sammenheng er ConfinementConcurrencyType. Apple har frarådet dette samtidighet type som på iOS 9. Dette betyr også at init () er frarådet fra og med iOS 9.

Det er to viktige metoder som ble lagt til Core data rammeverket når Apple introduserte foreldre /barn administrerte 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 PrivateQueueConcurrencyType 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 (_ :) metoden ikke blokkere den aktuelle tråden. 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 i rekkefølge.

For å avslutte denne artikkelen, vil jeg gjerne refactor Operasjon klassen til å dra nytte av foreldre /barn administrerte objekt sammenhenger. Du vil raskt merke at det forenkler NSOperation underklasse vi opprettet. The main () metoden endrer ganske mye. Ta en titt på sin oppdaterte gjennomføringen nedenfor
styre func main () {//Initial Managed Object Context privateManagedObjectContext = NSManagedObjectContext (concurrencyType: .PrivateQueueConcurrencyType). //Konfigurer Managed Object Context privateManagedObjectContext.parentContext = mainManagedObjectContext //gjøre noe arbeid //... hvis privateManagedObjectContext.hasChanges {gjøre {prøve privateManagedObjectContext.save ()} catch {//Feilhåndtering //...}}}

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.