10 alapelv a CoreData használatához anélkül, hogy szétrobbanna a fejed október 17, 2021 | Nincs hozzászólás vagy 10 csata megtanult CoreData leckékA CoreData egy objektumgráf-kezelő keretrendszerA CoreData különböző formátumokban tárolhatja az adatokatPár fogalom meghatározásaUse NSPersistentContainerHasználd a kapcsolatokatKerülje a stringes kódot, amennyire csak lehetségesTartsd az NSPersitentContainer.viewContext read-onlyVigyázz a szálakraA launch argumentumot használjuk a kontextus és a szálak követéséreAz aggregált lekérdezések használataTartsd a coreadatokkal kapcsolatos kódot kategóriákbanVállalj tranzakciós modellt az adataid módosításaihozNe mentsd el a kontextust egy modellkategória metódusából vagy 10 csata megtanult CoreData leckék Olivier Destrebecq Follow május 1, 2020 – 7 min read A CoreData-t több projekt során is használtam már az Apple platformos fejlesztői karrierem során (először a MacOS 10.5…), és az évek során a nehezebb úton tanultam meg dolgokat (aka: “túl sokáig vertem a fejem a falba”). Úgyhogy gondoltam, megosztom veletek a tanulságokat. Remélhetőleg megkíméllek néhány horzsolástól a homlokodon. Megjegyzem, hogy ez nem egy útmutató a CoreData beállításához. Célja, hogy egy cikk legyen néhány jó gyakorlattal, amit átvehetsz a projektjeidhez. Itt van az Apple gyors áttekintése: “Használd a CoreData-t az alkalmazásod állandó adatainak offline használatra történő mentéséhez, az ideiglenes adatok gyorsítótárba helyezéséhez, valamint az alkalmazásod visszavonási funkciójának egyetlen eszközön történő hozzáadásához.” A CoreData egy kicsit részletesebben: a CoreData az Apple technológiája a strukturált adatok helyi mentésére. A modellszerkesztővel definiálod az adatstruktúrádat, akárcsak egy adatbázis-séma szerkesztővel, majd ezeket az objektumokat használhatod a kódodban az adatok tárolására. A CoreData egy objektumgráf-kezelő keretrendszer Ez azt jelenti, hogy amikor létrehozod a modellt, definiálod az objektumaidat és azok kapcsolatait. Például, ha az adatmodelljében van egy Company (Vállalat) és egy Employee (Alkalmazott) entitás, akkor ezt a két entitást egy kapcsolat fogja összekapcsolni, amely összeköti a kettőt. Valószínűleg employees néven a Company entitáson és employer néven a Employee entitáson. Egyszerűen hozzáadja a kapcsolat tulajdonságot az entitásokhoz, és kész. Nem kell létrehoznia egy összekötő táblát, mint egy adatbázisban. A CoreData szépsége egy hagyományos adatbázishoz képest az, hogy amikor lekérdezi az X vállalatot, akkor a company.employees elérésével közvetlenül megkapja az alkalmazottakat. Nem kell join-lekérdezést készíteni vagy újra lekérdezni. A CoreData különböző formátumokban tárolhatja az adatokat Igen, jól hallotta, a CoreData különböző formátumokban/településeken tudja tárolni az adatokat. Mindegyiknek megvan a maga előnye és hátránya. Bináris tároló SQLite tároló Memórián belüli tároló Az egyes tárolók előnyeit és hátrányait ebben a cikkben olvashatja el. Én mindig SQLite-ot használok, de el tudom képzelni, hogy egy memórián belüli tároló szupergyors lenne. Pár fogalom meghatározása NSManagedObject: ez az alaposztály, amely a CoreData-ban tárolt objektumokat képviseli. Fetch request: ez a lekérdezés megfelelője a CoreData világában. Ezt a NSFetchedObject osztály képviseli. Use NSPersistentContainer Az iOS 10.0-ig a CoreData stack beállítása meglehetősen sok boilerplate kóddal járt. A CoreData többszálú használatához szükséges stratégia meghatározásához magadnak kellett megküzdened. A NSPersistentContainer-mal az Apple a boilerplate kód nagy részét kapszulázza. Megspórolja neked a vitát arról, hogyan állíthatod be a stacket az optimális teljesítmény érdekében. Ez a cikk tartalmazza az összes szükséges információt a helyes használathoz. Használd a kapcsolatokat Azért említem ezt mindig, mert két alkalommal, olyan projekteknél, amelyeket én örököltem, a CoreData modell úgy volt beállítva, mint egy adatbázis. Nem voltak kapcsolatok definiálva. Ehelyett a kapcsolódó objektum azonosítóját tárolták, és így a kapcsolódó objektum lekérdezéséhez külön lekérdezésre volt szükség. Szerintem sok fejlesztő úgy közelíti meg a CoreData-t, mint egy adatbázist, és elköveti ezt a hibát. Ez végül az egész projektet sokkal összetettebbé és lassabbá teszi. Ez a cikk segít az elindulásban. Kerülje a stringes kódot, amennyire csak lehetséges A stringes kód olyan kódra utal, amely karakterláncokat használ az adatok eléréséhez. Az iOS-en van néhány stringly API: UserDefaults, CoreData néhány, ami eszembe jut. Ha visszatérünk a céges példához, az alábbiakban két lehetőség van egy cég létrehozására és nevének beállítására: //stringly version to create and edit a companylet company = NSEntityDescription.insertNewObject(forEntityName: "Company", in: managedObjectContext)company.set("my less than great corporation", forKey: "name")//non stringly versionlet company = Company(in: managedObjectContext)company.name = "My super corporation" Előre mindkét lehetőség működik, lefordítják, nincs velük semmi baj… A stringly kóddal az a baj, hogy a fordító nem tud segíteni. Képzeld el, hogy a kódban több helyen is hozzá kell férned a name céghez, és reálisan legalább kettőre lesz szükséged: egy a beállításához és egy az olvasásához, most két helyen kell beírnod azt a name stringet. Persze létrehozhatsz egy változót a karakterlánc tárolására, és használhatod azt a változót, de ez extra erőfeszítést igényel, nem lesz beírva ellenőrizve, és mindannyian tudjuk, hogy a fejlesztők lusták… Szóval általában több helyen marad az a karakterlánc. Aztán át akarod nevezni, és le kell vadászni kereséssel és cserével… Ha egyet kihagysz, az alkalmazásod összeomlik. Itt van a lista azokról a fordítói funkciókról, amelyeket nem fogsz kihasználni a stringly szintaxissal: auto-complete refactor compiler warning in case of misspelling compiler error if you remove/renamed this entity or property in your model To be able to use the non-stringes kódváltozatot, egyszerűen meg kell kérnie a fordítót, hogy generáljon osztályokat az entitásokhoz (követheti az Apple utasításait a “Kódgenerálási opció kiválasztása” alatt). Mostantól ez az alapértelmezett egy új Entity létrehozásakor. Tartsd az NSPersitentContainer.viewContext read-only A teljesítmény miatt nem akarsz hosszú műveleteket végezni a viewContext-en, mivel az a főszálon fog futni, és esetleg blokkolja az UI-t. A megoldás az, hogy szokj hozzá, hogy csak olyan blokkban végezz módosításokat az objektumaidon, amelyet a NSPersistentContainer.performBackgroundTask() módszerrel ütemezel. Amikor elmented a módosításokat ebben a blokkban, azok visszaolvadnak a NSPersitentContainer.viewContext-ba, és frissítheted a felhasználói felületet. Vigyázz a szálakra A CoreData egyik fő húzása a szálbiztos használat. Bár ha egyszer megértetted a koncepciót, akkor elég könnyű. Az egész lényege, hogy minden adatobjektum, amit a CoreData-tól kapsz, csak abban a szálban használható, amelyhez az objektum managedobjectContext-je kapcsolódik. Egy módja annak, hogy biztosítsuk, hogy a megfelelő szálat használjuk az objektum eléréséhez, az NSManagedObjectContext.performBlockAndWait használata, amely biztosítja, hogy a kódunkat a megfelelő szálon használjuk. Amikor ebben a blokkban vagyunk, a NSManagedObject.objectID és a NSManagedObjectContext.existingObject(withId:) segítségével kell átadni az objektumot a szálakon és a kontextuson keresztül. Ha ezt nem tesszük, akkor nagy valószínűséggel holtpontra jutunk. Képzeljük el: Egy feladatot a főszálból ütemezünk be a Abban az esetben, ha valami a főszálból ütemezett NSManagedObjectContext.performBlockAndWait(). Ekkor a főszál blokkolva várja, hogy ez a blokk befejeződjön. A blokkban átadod a foo objektumot a fő kontextusból a bar() metódusnak, és elküldöd egy másik szálnak. Jó CoreData állampolgárként a bar() megkapja a foo-hoz csatolt kontextust és a foo.managedObjectContext.performBlockAndWait() Bumm, holtpontra kerültél, mivel ez a kontextus csak a főszálon hajtódik végre és a főszál vár ennek befejezésére. Egy másik konvenció, ami segít, hogy a nézetvezérlők között csak akkor adunk át objektumokat, ha azok a főszál kontextusában léteznek. A launch argumentumot használjuk a kontextus és a szálak követésére Ha követjük a fenti tippeket, akkor ez korlátozza annak az esélyét, hogy egy NSManagedObject példányt rossz szálon hívjunk meg. De ez még mindig előfordulhat. Ezért a fejlesztés során mindenképpen kapcsolja be a szálak hibakeresésére vonatkozó kijelentést -com.apple.CoreData.ConcurrencyDebug 1 Ez a cikk pontosan elmondja, hogyan kell ezt megtenni, és tájékoztat a további hibakeresés érdekében beállítható egyéb zászlókról. Az aggregált lekérdezések használata A CoreData a groupBy, sum és count aggregált függvényeket kínálja, amelyeket lekérdezés közben lehet futtatni. Ennek beállítása kissé bonyolult lehet, de megéri. Az alternatíva az, hogy létrehoz egy lekérdezést a kritériumoknak megfelelő összes objektum lekérdezéséhez, majd egy for-hurok futtatásával végigfuttatja az összes entitást, és maga végzi el a matematikát. Könnyebb kódolni, de sokkal kevésbé hatékony. Az aggregált lekérdezés futtatásakor a CoreData lefordítja azt adatbázis-lekérdezéssé, és a matematikát közvetlenül az adatbázis szintjén végzi el. Ez szupergyorsabbá teszi. Ez a cikk egy jó bevezető arról, hogyan kell ezt csinálni. Tartsd a coreadatokkal kapcsolatos kódot kategóriákban Mihelyt az Xcode létrehozta a NSManagedObject alosztályodat, valószínűleg rájössz, hogy van olyan kódod, amelynek az objektumodat reprezentáló osztályban kellene lennie (értékek kiszámításához, adatok importálásához a szerverről stb.). De megkérted a CoreData-t, hogy hozza létre helyetted az osztályokat… Szóval létre kell hoznod egy osztályt az objektumhoz, amelynek extra viselkedésre van szüksége. Csak a CoreData-val kapcsolatos kódot tedd bele, így tisztán tarthatod. Vállalj tranzakciós modellt az adataid módosításaihoz Módosíthatod az objektumaidat, amennyit csak akarod, de a változások perzisztenciájához meg kell hívnod a NSManagedObjectContext.save()-t. Még hamar elfelejted meghívni a save()-t, és lesz néhány lógó változás a környezetedben. Ezen a ponton minden fogadásnak vége, legközelebb, amikor meghívod a save()-t, nem leszel biztos benne, hogy mit is rögzítesz. Válaszd a tranzakciós modellt, hogy elkerüld a piszkos kontextus hátrahagyását. Ehhez hozzon létre egy kategóriát a NSPersistentContainer-on a következőképpen: extension NSPersistentContainer {@objc func performBackgroundSaveTask(_ block: @escaping (NSManagedObjectContext) -> Void) {performBackgroundTask { (context: NSManagedObjectContext) inblock(context)context.save()}}} Mostantól kezdve ügyeljen arra, hogy minden változtatását egy performBackgroundSaveTask()-val ütemezett blokkban hajtsa végre. Így garantált, hogy a módosítások akkor mentődnek el, amikor elvégezted őket. Ne mentsd el a kontextust egy modellkategória metódusából Ne hívd meg a save()-et egy entitáskategóriából. Nincs annál rosszabb, mint amikor úgy hívsz meg egy metódust az objektumodon, hogy nem veszed észre, hogy ezzel együtt a teljes kontextust is elmented. Néhány olyan változtatás átadása, amit nem biztos, hogy el akarsz menteni. A mentés meghívását azon a ponton kell elvégezni, ahol az objektum módosítását elkezdtük. Articles