- tai 10 taistelua CoreDatan opit
- CoreData on objektigraafin hallintakehys
- CoreData voi tallentaa tiedot eri muodoissa
- Pari termimääritelmää
- Käytä NSPersistentContainer
- Käytä suhteita
- Vältä string-koodia niin paljon kuin mahdollista
- Pitäkää NSPersitentContainer.viewContext read-only
- Vartioi noita säikeitä
- Käytä launch-argumenttia seurataksesi kontekstia ja säikeistystä
- Käytä aggregaattikyselyjä
- Säilytä CoreDataan liittyvä koodi luokissa
- Valitset transaktiomallin datan muutoksillesi
- Älä tallenna kontekstia malliluokan metodin sisältä
tai 10 taistelua CoreDatan opit
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.
- Binary store
- SQLite Store
- 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 metodiinbar()
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äenfoo.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.