het schrijven van Core Data code met prestaties in het achterhoofd helpt uw app voor te bereiden op de toekomst. Uw database kan klein zijn in het begin, maar kan gemakkelijk groeien, wat resulteert in trage query ‘ s en verminderde ervaring voor de gebruiker.
sinds ik begon met het schrijven van de Collect by WeTransfer app in 2017 heb ik veel Core Data gerelateerde code geschreven, het aanraken van het bijna elke dag. Met miljoenen gebruikers die veel content toevoegen, is het uitvoeren van Core Data gerelateerde code een belangrijke vaardigheid in ons team geworden.
in de loop der jaren hebben we veel inzichten ontwikkeld, die ik graag met u wil delen door middel van 5 tips die u moet weten.
- 1: Maak gebruik van een achtergrondbeheerde ObjectContext
- belangrijk: geef nsmanagedobject-instanties niet door tussen wachtrijen
- 2: sla alleen een beheerde ObjectContext op indien nodig
- overweeg zorgvuldig wanneer u uw wijzigingen
- 3: Alleen ophalen wat u nodig hebt
- 4: Maak gebruik van fetch limits
- 5: Verwijder veel objecten in één keer met behulp van een Nsbatchdeleterquest
- 6: weet hoe u Core Data code
- conclusie
1: Maak gebruik van een achtergrondbeheerde ObjectContext
een ding dat we vanaf het begin niet deden is gebruik maken van een achtergrondbeheerde ObjectContext. We gebruikten alleen de weergavecontext om kerngegevens gerelateerde taken uit te voeren: nieuwe inhoud invoegen, inhoud verwijderen, inhoud ophalen, enz.
in het begin was onze app relatief klein. Alleen gebruik maken van de view context was niet echt een probleem en resulteerde niet in een zichtbare prestatie sancties met betrekking tot kerngegevens. Uiteraard, zodra onze app begon te groeien, realiseerden we ons dat de weergave context werd geassocieerd met de hoofdwachtrij. Langzame query ‘ s blokkeerden onze gebruikersinterface en onze app werd minder reageren.
in het algemeen is het de beste praktijk om gegevens te verwerken op een achtergrondwachtrij omdat deze CPU-intensief kan zijn. Voorbeelden zoals het importeren van JSON in kerngegevens kunnen anders de weergave-context blokkeren en resulteren in niet-responsiviteit in de gebruikersinterface.
de oplossing is om gebruik te maken van een achtergrondbeheerde ObjectContext. De nieuwste API ‘ s maken het gemakkelijk om een nieuwe context te creëren vanuit uw persistente container:
let backgroundContext = persistentContainer.newBackgroundContext()
Ik adviseer deze methode over de NSManagedObjectContext(concurrenyType:)
initializer omdat het automatisch wordt geassocieerd met de NSPersistentStoreCoordinator
en het zal worden ingesteld om ook NSManagedObjectContextDidSave
uitzendingen te verbruiken. Dit houdt uw achtergrond context in sync met de weergave context.
u kunt deze achtergrondcontext opslaan in een aangepaste persistent container-subklasse. Op deze manier kun je je achtergrondcontext hergebruiken en hoef je maar twee contexten te beheren. Dit houdt uw core data structuur eenvoudig te begrijpen en het voorkomt het hebben van meerdere niet-gesynchroniseerde contexten.
als u de achtergrondcontext slechts op een paar plaatsen hoeft te gebruiken, kunt u ook beslissen om de performBackgroundTask(_:)
methode te gebruiken die een achtergrondcontext creëert:
persistentContainer.performBackgroundTask { (backgroundContext) in // .. Core Data Code}
deze methode maakt echter een nieuwe NSManagedObjectContext
aan telkens wanneer deze wordt aangeroepen. U kunt overwegen om de gedeelde achtergrondcontext te gebruiken als u vaker naar een achtergrondcontext verzendt.
belangrijk: geef nsmanagedobject-instanties niet door tussen wachtrijen
het schrijven van multi-threaded Core Data-code is veel complexer dan het gebruik van een enkele weergavecontext. De reden hiervoor is dat u een NSManagedObject
– instantatie van een weergavecontext niet zomaar kunt doorgeven aan een achtergrondcontext. Dit zou resulteren in een crash en potentiële data corruptie.
wanneer het nodig is om een beheerd object van de ene wachtrij naar de andere te verplaatsen, kunt u gebruik maken van de NSManagedObjectID
die thread-safe is:
let managedObject = NSManagedObject(context: persistentContainer.viewContext)backgroundContext.perform { let object = try? backgroundContext.existingObject(with: managedObject.objectID)}
2: sla alleen een beheerde ObjectContext op indien nodig
Als u een beheerde ObjectContext opslaat, worden alle huidige wijzigingen in het bovenliggende archief van de context vastgelegd. Zoals u zich kunt voorstellen, Dit is niet een goedkope operatie en het mag alleen worden gebruikt als dat nodig is om de prestaties in de kerngegevens te garanderen.
Allereerst is het belangrijk om te controleren of er zelfs maar iets op te slaan is. Als er geen wijzigingen zijn om te committen, is er ook geen reden om een opslag uit te voeren. Door een saveIfNeeded
methode aan te maken, kunt u dit eenvoudig inbouwen:
overweeg zorgvuldig wanneer u uw wijzigingen
opslaat behalve saveIfNeeded
in plaats van save()
moet u ook overwegen of een opslag zinvol is. Hoewel een context veranderingen kan hebben, is het niet altijd nodig om deze veranderingen direct te committen.
Als u bijvoorbeeld meerdere items in uw database belangrijk vindt, wilt u misschien alleen opslaan nadat u alle items in uw achtergrondcontext hebt geïmporteerd. Een Opslaan wordt vaak gevolgd door UI-updates en meerdere opslaan na elkaar kan gemakkelijk resulteren in onnodige herladen. Daarnaast, rekening houden met dat opgeslagen wijzigingen in een achtergrond context worden samengevoegd in de weergave context, het blokkeren van de hoofdwachtrij binnenkort ook. Wees daarom bewust!
3: Alleen ophalen wat u nodig hebt
Gegevens ophalen is een dure taak en moet zo performant mogelijk zijn om uw app klaar te maken voor grote datasets. De volgende code is een vaak gemaakte fout:
deze code laadt alle ingevoegde objecten in het geheugen terwijl het direct daarna wordt gefilterd om alleen inhoud met een naam te behouden.
het is veel beter predicaten te gebruiken om alleen de objecten op te halen die nodig zijn. Het bovenstaande filter kan worden geschreven als volgt met een NSPredicate
:
dit heeft twee voordelen:
- alleen de benodigde objecten worden in het geheugen geladen
- u hoeft niet over alle objecten te herhalen
predicaten zijn zeer flexibel en zouden u in de meeste gevallen in staat moeten stellen de gewenste dataset op te halen met behoud van de prestaties in de kerngegevens.
4: Maak gebruik van fetch limits
volgend op het vorige voorbeeld is het belangrijk om fetch limits in te stellen wanneer u slechts een deel van de dataset gaat weergeven.
stel bijvoorbeeld dat u alleen de eerste 3 namen van alle inhoudsitems nodig hebt. In dit geval zou het niet nodig zijn om alle inhoudsitems met een naam in het geheugen te laden. We kunnen dit voorkomen door een fetch-limiet in te stellen:
deze code retourneert alleen de eerste 3 inhoudsitems met een naam.
5: Verwijder veel objecten in één keer met behulp van een Nsbatchdeleterquest
in plaats van te herhalen in een dataset en elk object één voor één te verwijderen, is het vaak beter om een NSBatchDeleteRequest
te gebruiken die sneller werkt omdat het werkt op het sql-niveau in de persistent store zelf.
u kunt meer informatie vinden over batchverwijderingsverzoeken in mijn blogpost met behulp van Nsbatchdeleterquest om batches in kerngegevens te verwijderen.
6: weet hoe u Core Data code
moet debuggen zoals met alle code die u schrijft is het belangrijk om te weten hoe u deze kunt optimaliseren en debuggen als het niet werkt zoals verwacht. Er zijn vele manieren van debuggen die het best worden uitgelegd in mijn toegewijde blog post: Core Data Debugging in Xcode met behulp van launch argumenten.
conclusie
het schrijven van performant Core Data code vanaf het begin helpt u om uw app voor te bereiden op toekomstige grote datasets. Hoewel uw app zou kunnen presteren in het begin kan het gemakkelijk vertragen zodra uw database en model groeit. Door gebruik te maken van een achtergrondcontext, smart fetch-verzoeken en batch-verwijderverzoeken maakt u uw Core Data-code al performanter.
als u uw Swift-kennis wilt verbeteren, kijk dan op de Swift-categoriepagina. Neem gerust contact met me op of tweet naar me op Twitter als je extra tips of feedback hebt.
bedankt!