Skrive Kjernedatakode med ytelse i tankene bidrar til å forberede appen din for fremtiden. Databasen kan være liten i begynnelsen, men kan lett vokse, noe som resulterer i treg spørringer og redusert opplevelse for brukeren.
siden jeg begynte å skrive Collect by WeTransfer-appen i 2017, har jeg skrevet Mye Kjernedatarelatert kode, og berører det nesten hver dag. Med millioner av brukere som legger til mye innhold, har det å utføre kjernedatarelatert kode blitt en viktig ferdighet i teamet vårt.
gjennom årene har vi utviklet mye innsikt, som jeg er glad for å dele med deg gjennom 5 tips du bør vite.
- 1: Benytt deg av en bakgrunnsstyrt objektkontekst
- Viktig: ikke pass nsmanagedobject forekomster mellom køer
- 2: bare lagre en administrert objektkontekst hvis nødvendig
- Vurder nøye når du skal lagre endringene
- 3: Bare hente det du trenger
- 4: Bruk hentegrenser
- 5: Slett mange objekter samtidig ved hjelp Av En NSBatchDeleteRequest
- 6: Vet hvordan du feilsøker Kjernedatakode
- Konklusjon
1: Benytt deg av en bakgrunnsstyrt objektkontekst
En ting vi ikke gjorde fra starten, er å bruke en bakgrunnsstyrt objektkontekst. Vi brukte bare visningskonteksten til å utføre Noen Kjernedatarelaterte oppgaver: sette inn nytt innhold, slette innhold, hente innhold, etc.
i begynnelsen var appen vår relativt liten. Å bare bruke visningskonteksten var egentlig ikke et problem og resulterte ikke i noen synlige ytelsesstraffer knyttet til Kjernedata. Åpenbart, når vår app begynte å vokse, innså vi at visningskonteksten var knyttet til hovedkøen. Sakte spørringer blokkert VÅR UI og vår app ble mindre å svare.
generelt er beste praksis å utføre databehandling på en bakgrunnskø, da DEN kan VÆRE CPU-intensiv. Eksempler som å importere JSON Til Kjernedata kan ellers blokkere visningskonteksten og resultere i manglende respons i brukergrensesnittet.
løsningen er å benytte seg av en bakgrunnsstyrt objektkontekst. De nyeste Api-Ene gjør det enkelt å opprette en ny kontekst fra din faste beholder:
let backgroundContext = persistentContainer.newBackgroundContext()
jeg anbefaler denne metoden over NSManagedObjectContext(concurrenyType:)
initializer som det vil automatisk bli assosiert med NSPersistentStoreCoordinator
og det vil bli satt til å forbruke NSManagedObjectContextDidSave
sendinger også. Dette holder bakgrunnskonteksten synkronisert med visningskonteksten.
du kan lagre denne bakgrunnskonteksten på en egendefinert vedvarende beholderunderklasse. På denne måten kan du gjenbruke bakgrunnskonteksten din, og du trenger bare å håndtere to sammenhenger. Dette holder Kjernen datastruktur enkel å forstå, og det hindrer å ha flere ut av sync sammenhenger.
hvis du bare må bruke bakgrunnskonteksten noen få steder, kan du også velge å bruke metoden performBackgroundTask(_:)
som skaper en bakgrunnskontekst på plass:
persistentContainer.performBackgroundTask { (backgroundContext) in // .. Core Data Code}
denne metoden oppretter imidlertid en ny NSManagedObjectContext
hver gang den startes. Du vil kanskje vurdere å bruke den delte bakgrunnskonteksten hvis du sender oftere til en bakgrunnskontekst.
Viktig: ikke pass nsmanagedobject forekomster mellom køer
Skrive multi-threaded Kjerne datakode er mye mer komplisert enn å bruke en enkelt visning kontekst. Årsaken til dette er at du ikke bare kan passere en NSManagedObject
instantiert fra en visningskontekst til en bakgrunnskontekst. Å gjøre det vil resultere i en krasj og potensiell datakorrupsjon.
når det er nødvendig å flytte et administrert objekt fra en kø til en annen, kan du bruke NSManagedObjectID
som er trådsikker:
let managedObject = NSManagedObject(context: persistentContainer.viewContext)backgroundContext.perform { let object = try? backgroundContext.existingObject(with: managedObject.objectID)}
2: bare lagre en administrert objektkontekst hvis nødvendig
lagre en administrert objektkontekst forplikter alle gjeldende endringer i kontekstens overordnede lager. Som du kan forestille deg, er dette ikke en billig operasjon, og den bør bare brukes hvis det er nødvendig for å sikre ytelse i Kjernedata.
Først av alt er Det viktig å sjekke om det er noe å lagre. Hvis det ikke er noen endringer å forplikte, er det heller ingen grunn til å utføre en lagring. Ved å opprette en saveIfNeeded
metode tillater du deg selv å enkelt innebygd en sjekk for dette:
Vurder nøye når du skal lagre endringene
Bortsett fra å bruke saveIfNeeded
i stedet for save()
må du også vurdere om en lagring er fornuftig. Selv om en kontekst kan ha endringer, er det ikke alltid nødvendig å direkte begå disse endringene.
hvis du for eksempel er viktige flere elementer i databasen, vil du kanskje bare lagre etter at du har importert alle elementene i bakgrunnskonteksten. En lagring er ofte etterfulgt AV UI oppdateringer og flere lagrer etter hverandre kan lett resultere i unødvendige laster. Bortsett fra det, ta hensyn til at lagrede endringer i en bakgrunnskontekst slås sammen i visningskonteksten, og blokkerer hovedkøen kort tid også. Vær derfor bevisst!
3: Bare hente det du trenger
Henting av data er en dyr oppgave og må være så effektiv som mulig for å gjøre appen din forberedt på store datasett. Følgende kode er en ofte gjort feil:
denne koden vil laste alle innsatte objekter i minnet mens det blir filtrert rett etter å bare forbli med innhold som har et navn.
det er mye mer performant å bruke predikater for å bare hente objektene som trengs. Ovennevnte filter kan skrives som følger med en NSPredicate
:
Dette har to fordeler:
- bare de nødvendige objektene lastes inn i minnet
- Du trenger ikke å iterere over alle objekter
Predikater er svært fleksible og bør tillate deg å hente ønsket datasett i de fleste tilfeller mens du opprettholder ytelsen i Kjernedata.
4: Bruk hentegrenser
Etter det forrige eksemplet Er det viktig å angi hentegrenser når du bare skal vise en del av datasettet.
si for eksempel at du bare trenger de første 3 navnene på alle innholdselementer. I dette tilfellet ville det være unødvendig å laste inn alle innholdselementer som har et navn i minnet. Vi kan forhindre dette ved å sette en hentegrense:
denne koden returnerer bare de første 3 innholdselementene som har et navn.
5: Slett mange objekter samtidig ved hjelp Av En NSBatchDeleteRequest
I Stedet for å iterere over et datasett som sletter hvert objekt en etter en, er det ofte mer effektivt å bruke en NSBatchDeleteRequest
som går raskere når den opererer på SQL-nivå i selve vedvarende butikken.
du kan lære mer om forespørsler om batch-sletting i blogginnlegget Mitt Ved Hjelp Av NSBatchDeleteRequest for å slette grupper I Kjernedata.
6: Vet hvordan du feilsøker Kjernedatakode
Som med all kode du skriver, er det viktig å vite hvordan du optimaliserer og feilsøker det når det ikke fungerer som forventet. Det er mange måter å feilsøke på som er best forklart i mitt dedikerte blogginnlegg: Kjernedata Feilsøking I Xcode ved hjelp av lanseringsargumenter.
Konklusjon
Skrive performant Kjerne datakode fra begynnelsen hjelper deg å forberede din app for fremtidige store datasett. Selv om appen din kan utføre i begynnelsen, kan den lett avta når databasen og modellen vokser. Ved å gjøre bruk av en bakgrunn kontekst, smart hente forespørsler, og batch slette forespørsler du gjør Din Kjerne datakode allerede mer performant.
hvis du vil forbedre Swift-kunnskapen din, enda mer, sjekk Ut Swift-kategorisiden. Ta gjerne kontakt med Meg eller tweet Til Meg På Twitter hvis du har flere tips eller tilbakemeldinger.
Takk!