Inzicht in de verschillen tussen uw core Data model en beheerde objecten

gepubliceerd door donnywals op Oktober 5, 2020

het is u wellicht opgevallen dat wanneer Xcode uw NSManagedObject klassen genereert op basis van uw Core Data model bestand, de meeste eigenschappen van uw beheerde object optioneel zijn. Zelfs als je ze nodig hebt gemaakt in de model editor, zal Xcode een beheerd object genereren waar de meeste eigenschappen optioneel zijn.

In dit artikel zullen we dit fenomeen verkennen, en waarom het gebeurt.

het verkennen van gegenereerde nsmanagedobject-subklassen

wanneer u een project bouwt dat de automatische codegeneratie van Xcode gebruikt voor Kerngegevensmodellen, worden uw NSManagedObject – subklassen gegenereerd wanneer u uw project bouwt. Deze klassen zijn geschreven van uw project afgeleide data map en je moet ze niet direct wijzigen. De modellen die door Xcode worden gegenereerd, hebben optionele eigenschappen voor sommige eigenschappen die u aan uw entiteit hebt toegevoegd, ongeacht of u de eigenschap optioneel hebt gemaakt in de modelbewerker.

hoewel dit in het begin misschien vreemd klinkt, is het eigenlijk niet zo vreemd. Bekijk de volgende subklasse NSManagedObject :

extension ToDoItem { @nonobjc public class func fetchRequest() -> NSFetchRequest<ToDoItem> { return NSFetchRequest<ToDoItem>(entityName: "ToDoItem") } @NSManaged public var completed: Bool @NSManaged public var label: String?}

een van de twee eigenschappen voor mijn ToDoItem is optioneel, zelfs als ze beide vereist zijn in de model editor. Als ik een instantie van deze ToDoItem maak, gebruik ik de volgende code:

let item = ToDoItem(context: managedObjectContext)

de initializer van een beheerd object neemt een beheerde ObjectContext. Dit betekent dat ik geen waarde toewijst aan de beheerde eigenschappen tijdens de initialisatie van de ToDoItem. Het afdrukken van de waarde voor zowel de label en completed eigenschappen opbrengsten en interessant resultaat:

print(item.label) // nilprint(item.completed) // false

terwijl label nil is zoals verwacht, hebben kerngegevens een standaardwaarde van false toegewezen aan de eigenschap completed, wat zinvol is omdat Xcode een niet-optionele eigenschap genereerde voor completed. Laten we het een stap verder en neem een kijkje op de volgende code:

let item = ToDoItem(context: managedObjectContext)item.label = "Hello, item"print(item.label) // "Hello, item"print(item.completed) // falsedo { try managedObjectContext.save()} catch { print(error)}

wanneer u deze code uitvoert, zult u merken dat het de volgende uitvoer produceert:

Optional("Hello, item")falseError Domain=NSCocoaErrorDomain Code=1570 "completed is a required value." UserInfo={NSValidationErrorObject=<CoreDataExample.ToDoItem: 0x60000131c910> (entity: ToDoItem; id: 0x600003079900 <x-coredata:///ToDoItem/t1FABF4F1-0EF4-4CE8-863C-A815AA5C42FF2>; data: { completed = nil; label = "Hello, item";}), NSValidationErrorKey=completed, NSLocalizedDescription=completed is a required value.}

deze fout zegt duidelijk completed is a required value. wat betekent dat completed niet is ingesteld, en het afgedrukte beheerde object dat naast de foutmelding wordt getoond, toont ook dat completed nilis. Dus hoewel er een soort standaard waarde aanwezig is voor completed, wordt deze niet als niet-nil beschouwd totdat deze expliciet wordt toegewezen.

om te begrijpen wat er gebeurt, kunnen we een waarde toewijzen aan completed en de gedrukte beschrijving voor item nogmaals bekijken:

let item = ToDoItem(context: managedObjectContext)item.label = "Hello, item"print(item.completed)print(item)

deze code produceert de volgende uitvoer:

false<CoreDataExample.ToDoItem: 0x6000038749b0> (entity: ToDoItem; id: 0x600001b576e0 <x-coredata:///ToDoItem/tD27C9C9D-A676-4280-9D7C-A1E154B2AD752>; data: { completed = 0; label = "Hello, item";})

dit is best interessant, niet?

de eigenschap completed is gedefinieerd als een Bool, maar wordt afgedrukt als een getal. We kunnen de reden hiervoor vinden in de onderliggende SQLite store. De gemakkelijkste manier om het SQLite-bestand van uw Core Data store te verkennen is door -com.apple.CoreData.SQLDebug 1 als opstartargument aan uw app over te geven en het SQLITE-bestand te openen waarmee Core Data verbinding maakt in een sqlite explorer zoals SQLite database browser.

Tip:
Lees meer over argumenten voor het starten van kerngegevens in dit bericht.

wanneer u naar de schemadefinitie voor ZTODOITEM kijkt, zult u zien dat INTEGER wordt gebruikt als het type voor ZCOMPLETED. Dit betekent dat de eigenschap completed als een geheel getal wordt opgeslagen in de onderliggende SQLite-opslag. De reden completed wordt opgeslagen als een INTEGER is eenvoudig. SQLite heeft geen BOOLEAN type en gebruikt een INTEGER waarde van 0 voor false, en 1 voor true.

de data die u ziet afgedrukt wanneer u uw beheerde object instantie afdrukt, is niet de waarde voor uw completed eigenschap, het is de waarde voor completed die naar de SQLite store zal worden geschreven.

uit dit hoofdstuk kunnen twee dingen worden geleerd.

Ten eerste weet u nu dat er een mismatch is tussen de optionaliteit van uw gedefinieerde Core Data model en de gegenereerde beheerde objecten. Een niet-optionele String wordt weergegeven als een optionele String in uw gegenereerde model terwijl een niet-optionele Bool wordt weergegeven als een niet-optionele Bool in uw gegenereerde model.

ten tweede hebt u geleerd dat er een verschil is tussen hoe een waarde wordt weergegeven in uw beheerde objectmodel en hoe deze wordt weergegeven in de onderliggende SQLite-opslag. Om te zien welke waarden worden gebruikt om uw beheerde object instantie naar de onderliggende opslag te schrijven, kunt u het beheerde object afdrukken en het veld data lezen in de afgedrukte uitvoer.

de belangrijkste les hier is dat uw Core Data model in de model editor en uw beheerde object subklassen niet op dezelfde manier gegevens vertegenwoordigen. Optioneel in uw Core Data model betekent niet altijd optioneel in uw beheerde object subklasse en vice versa. Een niet-optionele waarde in uw Core Data model kan worden weergegeven als een optionele waarde in uw beheerde object subklasse. Core Data zal uw managed object valideren tegen zijn managed object model wanneer u probeert om het te schrijven naar de persistent store en gooi fouten als het tegenkomt validatie fouten.

dus waarom bestaat deze mismatch? Zou het niet veel gemakkelijker zijn als het managed object model en de managed object subklassen een directe mapping hadden?

het begrijpen van de mismatch tussen beheerde objecten en het Core Data model

een groot deel van de reden waarom er een mismatch is tussen uw beheerde objecten en het model dat u hebt gedefinieerd in de model editor komt uit Core Data ‘ s Objective-C roots.

aangezien Objective-C helemaal niet met Optional te maken heeft, is er niet altijd een goede mapping van de modeldefinitie naar Swift-code. Vaak lijkt de manier waarop de mapping werkt enigszins arbitrair. Bijvoorbeeld, Optional<String> en Optional<Bool> kunnen beide niet worden weergegeven als een type in Objective-C om de eenvoudige reden dat Optional niet bestaat in Objective-C. Swift en Objective-C kunnen echter met elkaar interopten en Optional<String> kunnen automatisch worden overbrugd naar een NSString. Helaas kan Optional<Bool> niet automatisch worden toegewezen aan iets in Objective-C omdat Xcode u zal vertellen wanneer u een @NSManaged eigenschap probeert te definiëren als Bool?.

Als u nog nooit met Objective-C hebt gewerkt, lijkt het u misschien heel vreemd dat er geen concept van Optionalbestaat. Hoe hebben mensen optionele eigenschappen gebruikt in Core Data voordat Swift? En wat gebeurt er als iets in Objective-C nil moet zijn?

in Objective-C is het prima als elke waarde nil is, zelfs als je het niet verwacht. En aangezien Core Data zijn wortels heeft in Objective-C een deel van deze erfenis draagt over aan uw gegenereerde Swift klassen in een soms minder dan ideale manier.

de belangrijkste afhaalmaaltijd is niet hoe Objective-C werkt, of hoe Xcode precies code genereert. In plaats daarvan wil ik dat je onthoudt dat de types en configuratie in je core Data model definitie niet (hoeven) overeen komen met de types in je (gegenereerde) beheerde object subklasse.

in samenvatting

in het artikel van deze week heb je veel geleerd over hoe je beheerde objectsubklassen en de definitie van het Kerngegevensmodel niet altijd op een rij staan zoals je zou verwachten. Je zag dat soms een niet-optionele eigenschap in de model editor als optioneel kan eindigen in de subklasse gegenereerde beheerde objecten, en andere keren eindigt het als een niet-optionele eigenschap met een standaardwaarde, zelfs als je zelf geen standaardwaarde hebt toegewezen.

u zag ook dat als er een standaardwaarde aanwezig is op een beheerde object instantie, dit niet betekent dat de waarde daadwerkelijk aanwezig is op het moment dat u uw beheerde object opslaat, tenzij u expliciet een standaardwaarde hebt gedefinieerd in de Core Data model editor.

hoewel dit zeker verwarrend en ongelukkig is, is Core Data vrij goed in het vertellen wat er mis is in de fouten die het gooit tijdens het opslaan van een beheerd object. Het is ook mogelijk om de waarden te inspecteren die Core Data zal proberen op te slaan door uw beheerde object instantie af te drukken en het data attribuut te inspecteren.

persoonlijk hoop ik dat het gedrag dat ik in het artikel van deze week heb beschreven, wordt behandeld in een toekomstige update van Core Data die het sneller vriendelijker maakt wanneer de beheerde object subklassen een nauwere, mogelijk directe mapping hebben naar het Core Data model dat is gedefinieerd in een model editor. Maar tot die tijd is het belangrijk om te begrijpen dat de model editor en je beheerde object subklassen je model niet op dezelfde manier vertegenwoordigen, en dat dit op zijn minst gedeeltelijk gerelateerd is aan de Objective-C roots van Core Data.

als u vragen, correcties of feedback over dit bericht heeft, laat het me dan weten op Twitter. Dit bericht is onderdeel van een deel van het onderzoek, exploratie en voorbereiding die ik doe voor een boek over kerngegevens die ik werk aan. Voor updates over dit boek zorg ervoor om me te volgen op Twitter. Ik ben momenteel van plan om het boek rond het einde van 2020 uit te brengen.

blijf op de hoogte met mijn wekelijkse nieuwsbrief

praktisch combineren

leer alles wat u moet weten over combineren en hoe u het kunt gebruiken in uw projecten met mijn nieuwe boek Praktisch combineren. Je krijgt dertien hoofdstukken, een speeltuin en een handvol voorbeeldprojecten om je te helpen zo snel mogelijk aan de slag te gaan met Combine.

het boek is beschikbaar als een digitale download voor slechts $ 29.99!

Haal Praktisch Combineren

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.