10 keskeistä periaatetta, joiden avulla voit käyttää CoreDataa räjäyttämättä päätäsi

Olen käyttänyt CoreDataa useiden projektien aikana Apple-alustan kehittäjäurani aikana (ensimmäinen kerta oli MacOS 10.5…), ja vuosien varrella olen oppinut asioita kantapään kautta (aka: ”hakkaamalla päätäni seinään aivan liian kauan”). Joten ajattelin jakaa oppimani kanssanne. Toivottavasti säästän sinut muutamalta mustelmalta otsallasi.

Huomaa, että tämä ei ole ohje CoreDatan käyttöönotosta. Sen on tarkoitus olla artikkeli, jossa on hyviä käytäntöjä, joita voit poimia projekteihisi.

Tässä on Applen nopea yleiskatsaus: ”Käytä CoreDataa sovelluksesi pysyvien tietojen tallentamiseen offline-käyttöä varten, väliaikaisten tietojen välimuistiin tallentamiseen ja peruuttamistoimintojen lisäämiseen sovellukseesi yhdellä laitteella.”

Hieman tarkemmin sanottuna CoreData on Applen tekniikka, jolla voit tallentaa strukturoitua dataa paikallisesti. Mallieditorin avulla määrittelet tietorakenteesi aivan kuten tietokannan skeemaeditorilla, minkä jälkeen voit käyttää näitä objekteja koodissasi tallentaaksesi tietosi.

CoreData on objektigraafin hallintakehys

Tämä tarkoittaa sitä, että luodessasi mallia määrittelet objektit ja niiden suhteet. Jos tietomallissasi on esimerkiksi Yritys-olio ja Työntekijä-olio, nämä kaksi oliota yhdistetään suhteella, joka yhdistää nämä kaksi. Luultavasti nimeltään employees oliolle Company ja employer oliolle Employee. Lisäät vain suhdeominaisuuden olioihin ja se on siinä. Sinun ei tarvitse luoda liitäntätaulukkoa kuten tietokannassa.

CoreDatan kauneus perinteiseen tietokantaan verrattuna on se, että kun teet kyselyn saadaksesi Yritys X:n, voit saada työntekijät suoraan yksinkertaisesti käyttämällä company.employees. Ei tarvitse rakentaa join-kyselyä tai tehdä uudelleen kyselyä.

CoreData voi tallentaa tiedot eri muodoissa

Kyllä, kuulit oikein, CoreData voi tallentaa tiedot eri muodoissa/paikoissa. Jokaisella on hyvät ja huonot puolensa.

  1. Binary store
  2. SQLite Store
  3. In Memory store

Voit lukea tämän artikkelin saadaksesi jokaisen hyvät ja huonot puolet. Itse käytän aina SQLiteä, mutta voisin nähdä, miten muistissa oleva tallennus olisi supernopea.

Pari termimääritelmää

NSManagedObject: tämä on perusluokka, joka edustaa CoreDataan tallennettuja objekteja.

Noutopyyntö: tämä vastaa kyselyä CoreDatan maailmassa. Sitä edustaa NSFetchedObject-luokka.

Käytä NSPersistentContainer

Kun iOS 10.0:een asti CoreData-pinon asettaminen sisälsi melko paljon boilerplate-koodia. Sinun piti itse määritellä strategia, jolla CoreDatan monisäikeinen käyttö hoidettiin.

NSPersistentContainer:n avulla Apple kapseloi suurimman osan boilerplate-koodista. Säästyt väittelyltä siitä, miten pino asetetaan parhaalla mahdollisella tavalla optimaalisen suorituskyvyn saavuttamiseksi.

Tämä artikkeli sisältää kaikki tiedot, joita tarvitset, jotta voit käyttää sitä oikein.

Käytä suhteita

Mainitsen tämän aina, koska kahdessa perimässäni projektissa CoreData-malli oli asetettu kuin tietokanta. Suhteita ei ollut määritelty. Sen sijaan siihen liittyvän objektin ID tallennettiin, ja näin ollen tarvittiin ylimääräinen noutopyyntö, jotta siihen liittyvä objekti saatiin. Luulen, että monet kehittäjät lähestyvät CoreDataa kuin tietokantaa ja tekevät tämän virheen. Tämä tekee lopulta koko projektista paljon monimutkaisemman ja paljon hitaamman.

Seuraa tätä artikkelia, jotta pääset alkuun.

Vältä string-koodia niin paljon kuin mahdollista

String-koodilla tarkoitetaan koodia, joka käyttää merkkijonoja tietojen käyttämiseen. iOS:ssä on muutama stringly API: UserDefaults, CoreData ovat muutamia, jotka tulevat mieleen.

Jos palaamme takaisin yritysesimerkkiin, alla on kaksi vaihtoehtoa luoda yritys ja asettaa sen nimi:

//stringly version to create and edit a company
let company = NSEntityDescription.insertNewObject(forEntityName: "Company", in: managedObjectContext)
company.set("my less than great corporation", forKey: "name")//non stringly version
let company = Company(in: managedObjectContext)
company.name = "My super corporation"

Ensimmäisellä silmäyksellä molemmat vaihtoehdot toimivat, ne kääntyvät, näissä ei ole mitään vikaa…

Ongelma stringly-koodissa on se, että kääntäjä ei voi auttaa sinua. Kuvittele, että sinun on päästävä käsiksi name yrityksen name:een useissa paikoissa koodissa, ja realistisesti ottaen tarvitset ainakin kaksi: yhden asettaaksesi sen ja toisen lukeaksesi sen, sinun on nyt kirjoitettava tuo name merkkijono kahteen paikkaan. Toki voit luoda muuttujan tallentamaan merkkijonon ja käyttää sitä muuttujaa, mutta se vaatii ylimääräistä vaivaa, sitä ei kirjoiteta tarkistettuna ja me kaikki tiedämme, että kehittäjät ovat laiskoja…

Siten tuo merkkijono jää yleensä useaan paikkaan. Sitten haluat nimetä sen uudelleen ja joudut metsästämään sen etsimällä ja korvaamalla… Jos unohdat yhden, sovelluksesi kaatuu.

Tässä on lista kääntäjän ominaisuuksista, joista et hyödy stringly-syntaksilla:

  • auto-complete
  • refactor
  • kääntäjän varoitus kirjoitusvirheen sattuessa
  • kääntäjän virheilmoitus, jos poistat/uudelleen nimeät tämän olion tai ominaisuuden mallissasi

Käyttääksesi non-stringly-versiota koodista, sinun on yksinkertaisesti pyydettävä kääntäjää tuottamaan luokat olioillesi (voit seurata Applen ohjeita kohdassa ”Select a Code Generation Option”). Tämä on nyt oletusarvo, kun luodaan uusi entiteetti.

Pitäkää NSPersitentContainer.viewContext read-only

Suorituskykysyistä et halua suorittaa pitkiä operaatioita viewContextissa, koska se suoritetaan pääsäikeessä ja mahdollisesti estää käyttöliittymän.

Ratkaisu on ottaa tavaksi tehdä muutoksia objekteihin vain lohkon sisällä, jonka ajoitat metodilla NSPersistentContainer.performBackgroundTask(). Kun tallennat muutokset tuossa lohkossa, ne yhdistetään takaisin NSPersitentContainer.viewContext:een ja voit päivittää käyttöliittymääsi.

Vartioi noita säikeitä

Yksi CoreDatan tärkeimmistä vetovoimatekijöistä on sen käyttäminen säiketurvallisesti. Tosin kun ymmärrät konseptin, se on melko helppoa.

Se tiivistyy siihen, että mitä tahansa CoreDatasta saatavaa dataobjektia voi käyttää vain siinä säikeessä, johon objektin managedobjectContext on liitetty.

Yksi tapa varmistaa, että käytät oikeaa säiettä objektin käyttämiseen, on käyttää NSManagedObjectContext.performBlockAndWait, joka varmistaa, että koodia käytetään oikeassa säikeessä.

Jos olet tuossa lohkossa, sinun täytyy käyttää NSManagedObject.objectID ja NSManagedObjectContext.existingObject(withId:) siirtääksesi objektin säikeiden ja kontekstin yli.

Jos et tee tätä, päädyt todennäköisesti umpikujaan. Kuvittele:

  • Ajoitat tehtävän pääsäikeestä käsin Tapauksessa, jossa jotain ajoitetaan pääsäikeestä NSManagedObjectContext.performBlockAndWait(). Tässä vaiheessa pääsäie on estynyt odottamaan tämän lohkon valmistumista.
  • Tässä lohkossa siirrät objektin foo pääkontekstista metodiin bar() ja lähetät sen toiselle säikeelle.
  • Ollakseen hyvä CoreData-kansalainen bar() saa foon liitetyn kontekstin ja lähettää tehtävänsä käyttäen foo.managedObjectContext.performBlockAndWait()
  • Boom, olet umpikujassa, koska tämä konteksti suoritetaan vain pääsäikeessä ja pääsäie odottaa tämän valmistumista.

Toinen konventio, joka auttaa, on objektien välittäminen näkymäohjainten välillä vain, jos ne ovat olemassa pääsäikeen kontekstissa.

Käytä launch-argumenttia seurataksesi kontekstia ja säikeistystä

Jos noudatat edellä mainittuja vinkkejä, tämän pitäisi rajoittaa mahdollisuutta, että NSManagedObject-instanssia kutsutaan väärässä säikeessä. Mutta näin voi silti tapahtua. Varmista siis kehitystyössä, että otat käyttöön säikeiden debuggausta koskevan väitteen

-com.apple.CoreData.ConcurrencyDebug 1

Tässä artikkelissa kerrotaan tarkalleen, miten se tehdään, ja kerrotaan muista lipputunnuksista, joita voit asettaa debuggauksen lisäämiseksi.

Käytä aggregaattikyselyjä

CoreData tarjoaa groupBy, sum ja count aggregaattifunktioita, jotka voidaan suorittaa kyselyn aikana. Sen määrittäminen voi olla hieman monimutkaista, mutta se on sen arvoista.

Vaihtoehtona on luoda kysely, jolla haetaan kaikki kriteerejä vastaavat objektit, ja ajaa sitten for-silmukka kaikkien olioiden läpi ja tehdä laskutoimitukset itse. Helpompi koodata, mutta paljon tehottomampi.

Kun suoritat aggregoidun hakupyynnön, CoreData kääntää sen tietokantakyselyksi ja suorittaa matematiikan suoraan tietokantatasolla. Se tekee siitä supernopeaa.

Tämä artikkeli on hyvä johdanto siihen, miten se tehdään.

Säilytä CoreDataan liittyvä koodi luokissa

Kun Xcode on luonut NSManagedObject-alaluokkasi, huomaat todennäköisesti, että sinulla on koodia, jonka pitäisi olla luokassa, joka edustaa objektiasi (arvojen laskemista, tietojen tuomista palvelimelta jne. varten). Mutta olet pyytänyt CoreDataa luomaan luokat puolestasi…

Siten sinun on luotava luokka objektille, joka tarvitsee ylimääräistä käyttäytymistä. Laita sinne vain CoreDataan liittyvää koodia, jotta voit pitää sen siistinä.

Valitset transaktiomallin datan muutoksillesi

Voit muokata objektejasi niin paljon kuin haluat, mutta pysyvöittääksesi nuo muutokset sinun täytyy kutsua NSManagedObjectContext.save().

Havaitset nopeasti unohtavasi kutsua save() ja sinulla on joitain roikkuvia muutoksia kontekstissasi. Tässä vaiheessa kaikki vedot ovat poissa, seuraavalla kerralla kun kutsut save(), et ole varma, mitä olet sitouttamassa.

Valitse transaktiomalli, jotta vältät likaisen kontekstin jättämisen jälkeesi. Luo tätä varten NSPersistentContainer:lle luokka seuraavasti:

extension NSPersistentContainer {
@objc func performBackgroundSaveTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
performBackgroundTask { (context: NSManagedObjectContext) in
block(context)
context.save()
}
}
}

Varmista tästä lähtien, että kaikki tekemäsi muutokset suoritetaan lohkossa, joka on ajoitettu performBackgroundSaveTask():lla. Näin varmistat, että muutokset tallentuvat silloin, kun teet ne.

Älä tallenna kontekstia malliluokan metodin sisältä

Älä koskaan kutsu save() olioluokan sisältä. Mikään ei ole pahempaa kuin kutsua metodia objektillesi tietämättä, että se tallentaa koko kontekstin sen mukana. Sitoutuminen joihinkin muutoksiin, joita et ehkä halua tallentaa. Tallennuksen kutsuminen tulisi tehdä siinä vaiheessa, kun objektin muutokset aloitetaan.