Core Data bestaat al jarenlang op OS X en het duurde niet lang voordat Apple het naar iOS bracht. Hoewel het framework niet zoveel aandacht krijgt als extensies of overdracht, blijft het jaar na jaar evolueren en dit jaar, met de release van iOS 8 en OS X Yosemite, is dit niet anders..
Apple introduceerde een paar nieuwe functies in het Core Data-framework, maar de meest opvallende zijn batchupdates en asynchroon ophalen. Ontwikkelaars vragen al vele jaren naar deze functies en Apple heeft eindelijk een manier gevonden om ze te integreren in Core Data. In deze zelfstudie laat ik u zien hoe batchupdates werken en wat ze betekenen voor het Core Data-framework.
Core Data is geweldig in het beheren van objectgrafieken. Zelfs complexe objectgrafieken met veel entiteiten en relaties zijn geen probleem voor Core Data. Core Data heeft echter enkele zwakke plekken en het bijwerken van een groot aantal records is daar een van.
Het probleem is gemakkelijk te begrijpen. Telkens wanneer u een record bijwerkt, laadt Core Data de record in het geheugen, werkt de record bij en slaat de wijzigingen op in de permanente opslag, bijvoorbeeld een SQLite-database..
Als Core Data een groot aantal records moet bijwerken, moet het elke afzonderlijke record in het geheugen laden, de record bijwerken en de wijzigingen naar de permanente store verzenden. Als het aantal records te groot is, wordt iOS eenvoudigweg uitverkocht vanwege een gebrek aan bronnen. Ook al heeft een apparaat met OS X mogelijk de bronnen om het verzoek uit te voeren, het zal traag zijn en veel geheugen verbruiken.
Een alternatieve aanpak is om de records in batches bij te werken, maar dat kost ook veel tijd en middelen. Op iOS 7 is dit de enige optie die iOS-ontwikkelaars hebben. Dat is niet langer het geval op iOS 8.
Op iOS 8 en OS X Yosemite is het mogelijk om rechtstreeks met de permanente winkel te praten en te vertellen wat u wilt wijzigen. Meestal betreft dit het bijwerken van een attribuut of het verwijderen van een aantal records. Apple noemt deze feature batch-updates.
Er zijn echter een aantal valkuilen om op te letten. Core Data doet veel dingen voor u en u beseft het misschien niet eens totdat u batchupdates gebruikt. Validatie is een goed voorbeeld. Omdat Core Data batchupdates rechtstreeks in de permanente opslag uitvoert, zoals een SQLite-database, kan Core Data geen validatie uitvoeren op de gegevens die u invoegt. Dit betekent dat u ervoor zorgt dat u geen ongeldige gegevens toevoegt aan de permanente winkel.
Wanneer zou u batchupdates gebruiken? Apple raadt aan om deze functie alleen te gebruiken als de traditionele aanpak te kostbaar of tijdrovend is. Als u honderden of duizenden e-mailberichten als gelezen wilt markeren, is batchupdates de beste oplossing op iOS 8 en OS X Yosemite.
Om te illustreren hoe batchupdates werken, stel ik voor dat we Done opnieuw bezoeken, een eenvoudige Core Data-applicatie die een takenlijst beheert. We voegen een knop toe aan de navigatiebalk die elk item in de lijst als voltooid markeert.
Download of kloont het project vanuit GitHub en open het in Xcode 6. Start de applicatie in de iOS Simulator en voeg een paar taakitems toe.
Open TSPViewController.m en een eigendom verklaren, checkAllButton
, van type UIBarButtonItem
in de privéklasse-extensie bovenaan.
@interface TSPViewController ()@property (strong, nonatomic) NSFetchedResultsController * fetchedResultsController; @property (strong, nonatomic) UIBarButtonItem * checkAllButton; @eigenschap (sterk, niet-atomisch) NSIndexPath * selectie; @einde
Initialiseer het item van de balkknop in de viewDidLoad
methode van de TSPViewController
class en stel het in als het item in de linkerknop van het navigatie-item.
// Initialize Check All Button self.checkAllButton = [[UIBarButtonItem alloc] initWithTitle: @ "Check All" -stijl: UIBarButtonItemStyleBordered target: self action: @selector (checkAll :)]; // Navigatie-item configureren self.navigationItem.leftBarButtonItem = self.checkAllButton;
checkall:
MethodeDe checkall:
methode is vrij eenvoudig, maar er zijn een paar kanttekeningen om op te letten. Bekijk de implementatie hieronder.
- (ongeldig) vink aanAlle: (id) afzender // Maak entiteitsbeschrijving NSEntityDescription * entityDescription = [NSEntityDescription entityForName: @ "TSPItem" inManagedObjectContext: self.managedObjectContext]; // Initialiseer Batch-updateverzoek NSBatchUpdateRequest * batchUpdateRequest = [[NSBatchUpdateRequest-toewijzing] initWithEntity: entityDescription]; // Batch-updateaanvraag configureren [batchUpdateRequest setResultType: NSUpdatedObjectIDsResultType]; [batchUpdateRequest setPropertiesToUpdate: @ @ "done": @YES]; // Voer batchverzoek uit NSError * batchUpdateRequestError = nil; NSBatchUpdateResult * batchUpdateResult = (NSBatchUpdateResult *) [self.managedObjectContext executeRequest: batchUpdateRequest error: & batchUpdateRequestError]; if (batchUpdateRequestError) NSLog (@ "Kon batch update-aanvraag niet uitvoeren"); NSLog (@ "% @,% @", batchUpdateRequestError, batchUpdateRequestError.localizedDescription); else // Object-ID's extraheren NSArray * objectIDs = batchUpdateResult.result; for (NSManagedObjectID * objectID in objectIDs) // Beheerde objecten in fouten veranderen NSManagedObject * managedObject = [self.managedObjectContext objectWithID: objectID]; if (managedObject) [self.managedObjectContext refreshObject: managedObject mergeChanges: NO]; // Fetch uitvoeren NSError * fetchError = nil; [self.fetchedResultsController performFetch: & fetchError]; if (fetchError) NSLog (@ "Kan fetch niet uitvoeren."); NSLog (@ "% @,% @" fetchError, fetchError.localizedDescription);
We beginnen met het maken van een NSEntityDescription
bijvoorbeeld voor de TSPItem entiteit en gebruik het om a te initialiseren NSBatchUpdateRequest
voorwerp.
// Entiteitsbeschrijving maken NSEntityDescription * entityDescription = [NSEntityDescription entityForName: @ "TSPItem" inManagedObjectContext: self.managedObjectContext]; // Initialiseer Batch-updateverzoek NSBatchUpdateRequest * batchUpdateRequest = [[NSBatchUpdateRequest-toewijzing] initWithEntity: entityDescription];
We hebben het resultaattype van het batchupdateverzoek ingesteld op NSUpdatedObjectIDsResultType
, wat betekent dat het resultaat van de batch-updateaanvraag een array zal zijn met de object-ID's, exemplaren van de NSManagedObjectID
klasse, van de records die zijn gewijzigd door het batchupdateverzoek.
// Batch-updateaanvraag configureren [batchUpdateRequest setResultType: NSUpdatedObjectIDsResultType];
We vullen ook het propertiesToUpdate
eigendom van het batch-updateverzoek. Voor dit voorbeeld stellen we in propertiesToUpdate
aan een NSDictionary
met één sleutel, @"gedaan"
, met waarde @JA
. Dit betekent eenvoudig dat elke TSPItem record wordt ingesteld op gedaan, dat is precies wat we willen.
// Configuratie van aanvraag voor batchupdate [batchUpdateRequest setPropertiesToUpdate: @ @ "done": @YES];
Hoewel batchupdates de beheerde objectcontext omzeilen, wordt het uitvoeren van een batchupdateverzoek gedaan door te bellen executeRequest: error:
op een NSManagedObjectContext
aanleg. Het eerste argument is een instantie van de NSPersistentStoreRequest
klasse. Om een batchupdate uit te voeren, passeren we de batchupdateaanvraag die we zojuist hebben gemaakt. Dit werkt vinnen sinds de NSBatchUpdateRequest
klasse is een NSPersistentStoreRequest
subklasse. Het tweede argument is een verwijzing naar een NSError
voorwerp.
// Voer batchverzoek uit NSError * batchUpdateRequestError = nil; NSBatchUpdateResult * batchUpdateResult = (NSBatchUpdateResult *) [self.managedObjectContext executeRequest: batchUpdateRequest error: & batchUpdateRequestError];
Zoals ik eerder al zei, omzeilen batchupdates de context van het beheerde object. Dit geeft batchupdates hun kracht en snelheid, maar het betekent ook dat de context van het beheerde object niet op de hoogte is van de wijzigingen die we hebben aangebracht. Om dit probleem te verhelpen, moeten we twee dingen doen:
Dit is wat we doen in de volgende regels van de checkall:
methode. We controleren eerst of het batchupdateverzoek succesvol was door de batchUpdateRequestError
voor nul
. Als dit lukt, halen we de array uit NSManagedObjectID
voorbeelden van de NSBatchUpdateResult
voorwerp.
// Extract Object ID's NSArray * objectIDs = batchUpdateResult.result;
We herhalen vervolgens de objectIDs
array en vraag de context van het beheerde object naar de bijbehorende NSManagedObject
aanleg. Als de context van het beheerde object een geldig beheerd object retourneert, veranderen we dit in een fout door aan te roepen refreshObject: MergeChanges:
, doorgeven van het beheerde object als eerste argument. Om het beheerde object tot een fout te dwingen, passeren we NEE
als het tweede argument.
// Extract Object ID's NSArray * objectIDs = batchUpdateResult.result; for (NSManagedObjectID * objectID in objectIDs) // Beheerde objecten in fouten veranderen NSManagedObject * managedObject = [self.managedObjectContext objectWithID: objectID]; if (managedObject) [self.managedObjectContext refreshObject: managedObject mergeChanges: NO];
De laatste stap is om de opgehaalde resultatencontroller een ophaalactie te laten uitvoeren om de gebruikersinterface bij te werken. Als dit niet lukt, loggen we de bijbehorende fout.
// Voer Fetch NSError * fetchError = nil; [self.fetchedResultsController performFetch: & fetchError]; if (fetchError) NSLog (@ "Kan fetch niet uitvoeren."); NSLog (@ "% @,% @" fetchError, fetchError.localizedDescription);
Hoewel dit omslachtig en redelijk ingewikkeld lijkt voor een eenvoudige bediening, moet u er rekening mee houden dat we de Core Data-stack omzeilen. Met andere woorden, we moeten zorgen voor een aantal zaken die gewoonlijk door Core Data voor ons worden gedaan.
Bouw het project en voer de applicatie uit in de iOS Simulator of op een fysiek apparaat. Klik of tik op het item van de balknop aan de rechterkant om elk taakobject in de lijst te controleren.
Batch-updates zijn een geweldige aanvulling op het Core Data-framework. Niet alleen beantwoordt het aan een behoefte die ontwikkelaars al vele jaren hebben, het is niet moeilijk om te implementeren zolang je een paar basisregels in gedachten houdt. In de volgende zelfstudie bekijken we asynchroon ophalen, een andere nieuwe functie van het Core Data-framework.