iOS 8 données de base et extraction asynchrone

Dans l'article précédent sur iOS 8 et Core Data, nous avons traité des mises à jour par lots. Les mises à jour par lots ne sont pas la seule nouvelle API en ville. Depuis iOS 8 et OS X Yosemite, il est possible d'extraire des données de manière asynchrone. Dans ce didacticiel, nous examinerons plus en détail comment implémenter la récupération asynchrone et dans quelles situations votre application peut tirer parti de cette nouvelle API..

1. Le problème

Comme les mises à jour par lots, l'extraction asynchrone fait partie de la liste de souhaits de nombreux développeurs depuis un certain temps déjà. Les demandes d'extraction peuvent être complexes et prendre une quantité de temps non négligeable. Pendant ce temps, la demande d'extraction bloque le thread sur lequel elle s'exécute et, par conséquent, bloque l'accès au contexte de l'objet géré qui exécute la demande d'extraction. Le problème est simple à comprendre, mais à quoi ressemble la solution Apple?.

2. La solution

La réponse d'Apple à ce problème est l'extraction asynchrone. Une demande d'extraction asynchrone s'exécute en arrière-plan. Cela signifie qu'il ne bloque pas d'autres tâches pendant son exécution, telles que la mise à jour de l'interface utilisateur sur le thread principal..

La récupération asynchrone intègre également deux autres fonctionnalités pratiques, le compte rendu des progrès et l'annulation. Une demande d'extraction asynchrone peut être annulée à tout moment, par exemple lorsque l'utilisateur décide que l'exécution de la demande d'extraction est trop longue. Le rapport d'avancement est un ajout utile pour montrer à l'utilisateur l'état actuel de la demande d'extraction..

L'extraction asynchrone est une API flexible. Non seulement il est possible d'annuler une demande d'extraction asynchrone, mais il est également possible d'apporter des modifications au contexte de l'objet géré pendant l'exécution de la demande d'extraction asynchrone. En d'autres termes, l'utilisateur peut continuer à utiliser votre application pendant qu'elle exécute une requête d'extraction asynchrone en arrière-plan..

3. Comment ça marche?

Comme les mises à jour par lots, les demandes d’extraction asynchrones sont transmises au contexte de l’objet géré en tant que NSPersistentStoreRequest objet, une instance du NSAsynchronousFetchRequest classe pour être précis.

Un NSAsynchronousFetchRequest instance est initialisée avec un NSFetchRequest objet et un bloc d'achèvement. Le bloc d'achèvement est exécuté lorsque la demande d'extraction asynchrone a terminé sa demande d'extraction..

Revoyons l’application de tâches que nous avons créée précédemment dans cette série et remplaçons l’implémentation actuelle de NSFetchedResultsController classe avec une requête d'extraction asynchrone.

Étape 1: Configuration du projet

Téléchargez ou clonez le projet depuis GitHub et ouvrez-le dans Xcode 6. Avant de commencer à utiliser le NSAsynchronousFetchRequest classe, nous devons faire des changements. Nous ne pourrons pas utiliser le NSFetchedResultsController classe de gestion des données de la vue de table depuis la NSFetchedResultsController la classe a été conçue pour fonctionner sur le fil principal.

Étape 2: remplacement du contrôleur de résultats récupérés

Commencez par mettre à jour l’extension de classe privée du TSPViewController classe comme indiqué ci-dessous. On enlève le fetchedResultsController propriété et créer une nouvelle propriété, articles, de type NSArray pour stocker les articles à faire. Cela signifie également que le TSPViewController la classe n'a plus besoin de se conformer à la NSFetchedResultsControllerDelegate protocole.

@interface TSPViewController () @property (éléments forts, non atomiques) NSArray * items; @property (strong, nonatomic) NSIndexPath * sélection; @fin

Avant de refactoriser le viewDidLoad méthode, je veux d’abord mettre à jour la mise en œuvre du UITableViewDataSource protocole. Regardez les modifications que j'ai apportées dans les blocs de code suivants.

- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView return self.items? dix; 
- (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) section return self.items? self.items.count: 0; 
- (void) configureCell: (TSPToDoCell *) cellule atIndexPath: (NSIndexPath *) indexPath // Récupération d'un enregistrement NSManagedObject * record = [self.items objectAtIndex: indexPath.row]; // Update Cell [cell.nameLabel setText: [record valueForKey: @ "name"]]; [cell.doneButton setSelected: [[record valueForKey: @ "done"] boolValue]]; [cell setDidTapButtonBlock: ^ BOOL isDone = [[record valueForKey: @ "done"] boolValue]; // Mise à jour de l'enregistrement [record setValue: @ (! IsDone) forKey: @ "done"]; ]; 
- (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) éditionStyle forRowAtIndexPath: (NSIndexPath *) indexPath si (editStyle == UITableViewCellEditingSdleSite) if (record) [self.managedObjectContext deleteObject: record]; 

Nous devons également modifier une ligne de code dans le prepareForSegue: expéditeur: méthode comme indiqué ci-dessous.

// Extraire l'enregistrement NSManagedObject * record = [self.items objectAtIndex: self.selection.row];

Dernier point mais non le moindre, supprimer la mise en œuvre de la NSFetchedResultsControllerDelegate protocole puisque nous n'en avons plus besoin.

Étape 3: Création de la demande d'extraction asynchrone

Comme vous pouvez le voir ci-dessous, nous créons la demande d'extraction asynchrone dans le contrôleur de vue. viewDidLoad méthode. Prenons un moment pour voir ce qui se passe.

- (void) viewDidLoad [super viewDidLoad]; // Helpers __weak TSPViewController * faibleSelf = self; // Initialise la requête de récupération NSFetchRequest * fetchRequest = [[NSFetchRequest alloc]] initWithEntityName: @ "TSPItem"]; // Ajouter des descripteurs de tri [fetchRequest setSortDescriptors: @ [[NSSortDescriptor sortDescriptorWithKey: @ "createdAt" ascending: YES]]]; // Initialize As Requis Fetch NSAsynchronousFetchRequest * asynchronousFetchRequest = [[NSAsynchronousFetchRequ alloc] initWithFetchRequest: fetchRequest (voir la liste des articles en anglais) ; ]; // Exécuter une requête d'extraction asynchrone [self.managedObjectContext performBlock: ^ // Exécuter une requête d'extraction asynchrone NSError * asynchronousFetchRequestError = nil; NSAsynchronousFetchResult * asynchronousFetchResult = (erreur NSAsynchronousFetchResult *) [faiblesseSelf.managedObjectContext executeRequest: asynchronousFetchRequest: & asynchronousFetchRequestError]; if (asynchronousFetchRequestError) NSLog (@ "Impossible d'exécuter le résultat d'extraction asynchrone."); NSLog (@ "% @,% @", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription); ]; 

Nous commençons par créer et configurer un NSFetchRequest instance pour initialiser la demande d’extraction asynchrone. C'est cette demande d'extraction que la demande d'extraction asynchrone sera exécutée en arrière-plan.

// Initialise la requête de récupération NSFetchRequest * fetchRequest = [[NSFetchRequest alloc]] initWithEntityName: @ "TSPItem"]; // Ajouter des descripteurs de tri [fetchRequest setSortDescriptors: @ [[NSSortDescriptor sortDescriptorWithKey: @ "createdAt" ascending: YES]]];

Pour initialiser un NSAsynchronousFetchRequest par exemple, nous invoquons initWithFetchRequest: completionBlock:, en passant fetchRequest et un bloc d'achèvement.

// Initialize As Requis Fetch NSAsynchronousFetchRequest * asynchronousFetchRequest = [[NSAsynchronousFetchRequ alloc] initWithFetchRequest: fetchRequest (voir la liste des articles en anglais) ; ];

Le bloc d'achèvement est appelé lorsque la demande d'extraction asynchrone a terminé l'exécution de sa demande d'extraction. Le bloc d'achèvement prend un argument de type NSAsynchronousFetchResult, qui contient le résultat de la requête ainsi qu'une référence à la demande d'extraction asynchrone d'origine.

Dans le bloc d'achèvement, nous invoquons processAsynchronousFetchResult:, en passant dans le NSAsynchronousFetchResult objet. Nous allons jeter un oeil à cette méthode d'assistance dans quelques instants.

L’exécution de la demande d’extraction asynchrone est presque identique à la façon dont nous exécutons une NSBatchUpdateRequest. Nous appelons executeRequest: error: sur le contexte de l'objet géré, en transmettant la demande d'extraction asynchrone et un pointeur à un NSError objet.

[self.managedObjectContext performBlock: ^ // Exécuter une demande d'extraction asynchrone NSError * asynchronousFetchRequestError = nil; NSAsynchronousFetchResult * asynchronousFetchResult = (erreur NSAsynchronousFetchResult *) [faiblesseSelf.managedObjectContext executeRequest: asynchronousFetchRequest: & asynchronousFetchRequestError]; if (asynchronousFetchRequestError) NSLog (@ "Impossible d'exécuter le résultat d'extraction asynchrone."); NSLog (@ "% @,% @", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription); ];

Notez que nous exécutons la demande d'extraction asynchrone en appelant performBlock: sur le contexte de l'objet géré. Bien que cela ne soit pas strictement nécessaire puisque le viewDidLoad La méthode, dans laquelle nous créons et exécutons la requête d'extraction asynchrone, est appelée sur le thread principal. C'est une bonne habitude et une bonne pratique de le faire..

Même si la demande d’extraction asynchrone est exécutée en arrière-plan, notez que la executeRequest: error: la méthode retourne immédiatement, nous donnant un NSAsynchronousFetchResult objet. Une fois la demande d’extraction asynchrone terminée, le même NSAsynchronousFetchResult l'objet est rempli avec le résultat de la requête d'extraction.

Enfin, nous vérifions si la demande d’extraction asynchrone a été exécutée sans problème en vérifiant si la NSError objet est égal à néant.

Étape 4: Traitement du résultat de l'extraction asynchrone

le processAsynchronousFetchResult: La méthode n'est rien de plus qu'une méthode d'assistance dans laquelle nous traitons le résultat de la demande d'extraction asynchrone. Nous définissons le contrôleur de vue articles propriété avec le contenu du résultat résultat final propriété et recharger la vue table.

- (void) processAsynchronousFetchResult: (NSAsynchronousFetchResult *) asynchronousFetchResult if (asynchronousFetchResult.finalResult) // Mettre à jour des éléments [self setItems: asynchronousFetchResult.finalResult]; // Reload Table View [self.tableView reloadData]; 

Étape 5: Construire et exécuter

Générez le projet et exécutez l'application dans le simulateur iOS. Vous serez peut-être surpris de voir votre application se bloquer lorsque celle-ci tente d'exécuter la demande d'extraction asynchrone. Heureusement, la sortie de la console nous dit ce qui ne va pas.

*** Fermeture de l'application en raison d'une exception non interceptée 'NSInvalidArgumentException', raison: 'contexte NSConfinementConcurrencyType  ne peut pas prendre en charge la requête d'extraction asynchrone  avec demande de récupération  (entity: TSPItem; prédicat: ((null)); sortDescriptors: (("(createdAt, ascending, compare :)")); type: NSManagedObjectResultType;). '

Si vous n'avez pas lu l'article sur Core Data et la concurrence, vous risquez d'être confus par ce que vous lisez. Rappelez-vous que Core Data déclare trois types de concurrence, NSConfinementConcurrencyTypeNSPrivateQueueConcurrencyType, et NSMainQueueConcurrencyType. Chaque fois que vous créez un contexte d'objet géré en appelant la classe init méthode, le type de simultanéité du contexte d'objet géré résultant est égal à NSConfinementConcurrencyType. C'est le type de concurrence par défaut.

Le problème, cependant, est que la récupération asynchrone est incompatible avec la NSConfinementConcurrencyType type. Sans entrer trop dans les détails, il est important de savoir que la demande d'extraction asynchrone doit fusionner les résultats de sa demande d'extraction avec le contexte de l'objet géré qui a exécuté la demande d'extraction asynchrone. Il doit savoir sur quelle file d’attente il peut le faire et c’est pourquoi seul NSPrivateQueueConcurrencyType et NSMainQueueConcurrencyType supporte la récupération asynchrone. La solution est très simple si.

Étape 6: Configuration du contexte d'objet géré

Ouvrir TSPAppDelegate.m et mettre à jour le managedObjectContext méthode comme indiqué ci-dessous.

- (NSManagedObjectContext *) managedObjectContext if (_managedObjectContext) return _managedObjectContext;  NSPersistentStoreCoordinator * coordinator = [self persistentStoreCoordinator]; if (coordinateur) _managedObjectContext = [[NSManagedObjectContext alloc]] initWithConcurrencyType: NSMainQueueConcurrencyType]; [_managedObjectContext setPersistentStoreCoordinator: coordinator];  return _managedObjectContext; 

Le seul changement que nous avons apporté consiste à remplacer le init méthode avec initWithConcurrencyType:, en passant NSMainQueueConcurrencyType comme argument. Cela signifie que le contexte de l'objet géré ne doit être accessible qu'à partir du thread principal. Cela fonctionne bien tant que nous utilisons le performBlock: ou performBlockAndWait: méthodes pour accéder au contexte d'objet géré.

Exécutez le projet une fois de plus pour vous assurer que notre modification a effectivement résolu le problème..

4. Montrer les progrès

le NSAsynchronousFetchRequest La classe ajoute un support pour surveiller la progression de la requête d'extraction et il est même possible d'annuler une requête d'extraction asynchrone, par exemple, si l'utilisateur décide que l'opération prend trop de temps..

le NSAsynchronousFetchRequest la classe tire parti de la NSProgress classe pour le rapport d'avancement ainsi que pour l'annulation d'une demande d'extraction asynchrone. le NSProgress La classe, disponible depuis iOS 7 et OS X 10.9, est un moyen astucieux de surveiller la progression d'une tâche sans qu'il soit nécessaire de la lier étroitement à l'interface utilisateur..

le NSProgress La classe prend également en charge l'annulation, qui permet d'annuler une demande d'extraction asynchrone. Voyons ce que nous devons faire pour implémenter le rapport d'avancement pour la demande d'extraction asynchrone..

Étape 1: Ajout de SVProgressHUD

Nous montrerons à l'utilisateur l'état d'avancement de la demande d'extraction asynchrone à l'aide de la bibliothèque SVProgressHUD de Sam Vermette. Téléchargez la bibliothèque depuis GitHub et ajoutez le SVProgressHUD dossier de votre projet Xcode.

Étape 2: Mise en place NSProgress 

Dans cet article, nous n'explorerons pas NSProgress classe en détail, mais n'hésitez pas à en savoir plus dans la documentation. Nous créons un NSProgress par exemple dans le bloc, nous tendons à la performBlock: méthode dans le contrôleur de vue viewDidLoad méthode.

// Créer une progression NSProgress * progress = [NSProgress progressWithTotalUnitCount: 1]; // Devenir actuel [progrès devenirCurrentWithPendingUnitCount: 1];

Vous pourriez être surpris que nous ayons défini le nombre total d'unités sur 1. La raison est simple. Lorsque Core Data exécute la demande d’extraction asynchrone, il ne sait pas combien d’enregistrements il trouvera dans le magasin persistant. Cela signifie également que nous ne pourrons pas montrer la progression relative à l'utilisateur - un pourcentage. Au lieu de cela, nous montrerons à l'utilisateur l'état d'avancement absolu - le nombre d'enregistrements trouvés.

Vous pouvez remédier à ce problème en effectuant une demande d'extraction pour extraire le nombre d'enregistrements avant d'exécuter la demande d'extraction asynchrone. Toutefois, je préfère ne pas le faire, car cela signifie également que l'extraction des enregistrements à partir du magasin persistant prend plus de temps à cause de la demande d'extraction supplémentaire au début..

Étape 3: Ajout d'un observateur

Lorsque nous exécutons la demande d’extraction asynchrone, nous recevons immédiatement un NSAsynchronousFetchResult objet. Cet objet a un le progrès propriété, qui est de type NSProgress. C'est ça le progrès propriété que nous devons observer si nous voulons recevoir des mises à jour de progression.

// Exécuter une requête d'extraction asynchrone [self.managedObjectContext performBlock: ^ // Create Progress NSProgress * progress = [NSProgress progressWithTotalUnitCount: 1]; // Devenir actuel [progrès devenirCurrentWithPendingUnitCount: 1]; // Exécuter une demande d'extraction asynchrone NSError * asynchronousFetchRequestError = nil; NSAsynchronousFetchResult * asynchronousFetchResult = (NSAsynchronousFetchResult *) [self.managedObjectContext executeRequest: erreur asynchronousFetchRequest: & asynchronousFetchRequestError]; if (asynchronousFetchRequestError) NSLog (@ "Impossible d'exécuter le résultat d'extraction asynchrone."); NSLog (@ "% @,% @", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription);  // Add Observer [asynchronousFetchResult.progress addObserver: self forKeyPath: @ options "completedUnitCount": NSKeyValueObservingOptionNew context: ProgressContext]; // Resign Current [progress resignCurrent]; ];

Notez que nous appelons resignCurrent sur le le progrès objecter pour équilibrer le plus tôt devenirCurrentWithPendingUnitCount: appel. N'oubliez pas que ces deux méthodes doivent être appelées sur le même thread..

Étape 4: retrait de l'observateur

Dans le bloc d'achèvement de la demande d'extraction asynchrone, nous supprimons l'observateur et le HUD de progression..

// Initialize As Requis Fetch NSAsynchronousFetchRequest * asynchronousFetchRequest = [[NSAsynchronousFetchRequ alloc] initWithFetchRequest: fetchRequest: faire de la sécurité dans une section en cours de préparation Result [processus faibleSelfAsynchronousFetchResult: result]; // Remove Observer [résultat.progress removeObserver: faibleSelf pourKeyPath: @ "completedUnitCount" contexte: ProgressContext];); ];

Avant de mettre en œuvre observeValueForKeyPath: ofObject: change: context:, nous devons ajouter une instruction d'importation pour la bibliothèque SVProgressHUD, déclarer la variable statique ProgressContext que nous transmettons en tant que contexte lors de l'ajout et de la suppression de l'observateur, et affichons la palette de progression avant de créer la demande d'extraction asynchrone.

#import "SVProgressHUD / SVProgressHUD.h"
statique vide * ProgressContext = & ProgressContext;
- (void) viewDidLoad [super viewDidLoad]; // Helpers __weak TSPViewController * faibleSelf = self; // Afficher la palette de progression [SVProgressHUD showWithStatus: @ "Récupération de données" maskType: SVProgressHUDMaskTypeGradient]; // Initialise la requête de récupération NSFetchRequest * fetchRequest = [[NSFetchRequest alloc]] initWithEntityName: @ "TSPItem"]; // Ajouter des descripteurs de tri [fetchRequest setSortDescriptors: @ [[NSSortDescriptor sortDescriptorWithKey: @ "createdAt" ascending: YES]]]; // Initialize As Requis Fetch NSAsynchronousFetchRequest * asynchronousFetchRequest = [[NSAsynchronousFetchRequ alloc] initWithFetchRequest: fetchRequest: faire de la sécurité dans une section en cours de préparation Result [processus faibleSelfAsynchronousFetchResult: result]; // Remove Observer [résultat.progress removeObserver: faibleSelf pourKeyPath: @ "completedUnitCount" contexte: ProgressContext];); ]; // Exécuter une requête d'extraction asynchrone [self.managedObjectContext performBlock: ^ // Create Progress NSProgress * progress = [NSProgress progressWithTotalUnitCount: 1]; // Devenir actuel [progrès devenirCurrentWithPendingUnitCount: 1]; // Exécuter une demande d'extraction asynchrone NSError * asynchronousFetchRequestError = nil; NSAsynchronousFetchResult * asynchronousFetchResult = (erreur NSAsynchronousFetchResult *) [faiblesseSelf.managedObjectContext executeRequest: asynchronousFetchRequest: & asynchronousFetchRequestError]; if (asynchronousFetchRequestError) NSLog (@ "Impossible d'exécuter le résultat d'extraction asynchrone."); NSLog (@ "% @,% @", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription);  // Add Observer [asynchronousFetchResult.progress addObserver: self forKeyPath: @ options "completedUnitCount": NSKeyValueObservingOptionNew context: ProgressContext]; // Resign Current [progress resignCurrent]; ]; 

Étape 5: Rapport d'avancement

Tout ce qu'il nous reste à faire, c'est de mettre en œuvre le observeValueForKeyPath: ofObject: change: context: méthode. Nous vérifions si le contexte est égal à ProgressContext, créer un statut objet en extrayant le nombre d'enregistrements terminés à partir du changement dictionnaire, et mettre à jour le HUD de progression. Notez que nous mettons à jour l'interface utilisateur sur le thread principal.

- (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (id) changement d'objet: (NSDictionary *) change le contexte: (void *) context if (context == ProgressContext) dispatch_async (dispatch_get_main_queue (), ^ // Create Status NSString * status = [NSString stringWithFormat: @ "Enregistrements% li extraits", (long) [[change objectForKey: @ "new"] integerValue]] // Afficher la palette de progression [SVProgressHUD setStatus: status];); 

5. Données factices

Si nous voulons tester correctement notre application, nous avons besoin de plus de données. Bien que je ne recommande pas d'utiliser l'approche suivante dans une application de production, c'est un moyen rapide et facile de remplir la base de données avec des données..

Ouvrir TSPAppDelegate.m et mettre à jour le application: didFinishLaunchingWithOptions: méthode comme indiqué ci-dessous. le populateDatabase méthode est une méthode d'assistance simple dans laquelle nous ajoutons des données factices à la base de données.

- Application (BOOL): application (UIApplication *) application didFinishLaunchingWithOptions: (NSDictionary *) launchOptions // Remplir la base de données [self populateDatabase];… retourner YES; 

La mise en œuvre est simple. Parce que nous voulons uniquement insérer des données factices une fois, nous vérifions la clé de la base de données par défaut de l'utilisateur. @ "didPopulateDatabase". Si la clé n'est pas définie, nous insérons des données factices.

- (void) populateDatabase // Helpers NSUserDefaults * ud = [NSUserDefaults standardUserDefaults]; if ([ud objectForKey: @ "didPopulateDatabase"]) return; pour (NSInteger i = 0; i < 1000000; i++)  // Create Entity NSEntityDescription *entity = [NSEntityDescription entityForName:@"TSPItem" inManagedObjectContext:self.managedObjectContext]; // Initialize Record NSManagedObject *record = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext]; // Populate Record [record setValue:[NSString stringWithFormat:@"Item %li", (long)i] forKey:@"name"]; [record setValue:[NSDate date] forKey:@"createdAt"];  // Save Managed Object Context [self saveManagedObjectContext]; // Update User Defaults [ud setBool:YES forKey:@"didPopulateDatabase"]; 

 Le nombre d'enregistrements est important. Si vous envisagez d'exécuter l'application sur le simulateur iOS, vous pouvez insérer 100 000 ou 1 000 000 enregistrements. Cela ne fonctionnera pas aussi bien sur un périphérique physique et prendra trop de temps à compléter..

dans le pour boucle, nous créons un objet géré et le remplissons de données. Notez que nous ne sauvegardons pas les modifications du contexte de l’objet géré à chaque itération du pour boucle.

Enfin, nous mettons à jour la base de données par défaut de l'utilisateur pour nous assurer qu'elle ne sera pas renseignée lors du prochain lancement de l'application..

Génial. Exécutez l'application dans le simulateur iOS pour voir le résultat. Vous remarquerez qu'il faut quelques instants à la demande d'extraction asynchrone pour lancer l'extraction des enregistrements et mettre à jour le HUD de progression..

6. Changements radicaux

En remplaçant la classe du contrôleur de résultats récupérés par une demande d'extraction asynchrone, nous avons cassé quelques éléments de l'application. Par exemple, taper sur la coche d'une tâche ne semble plus fonctionner. Pendant la mise à jour de la base de données, l'interface utilisateur ne reflète pas le changement. La solution est assez facile à résoudre et je vous laisse le soin de mettre en œuvre une solution. Vous devez maintenant avoir suffisamment de connaissances pour comprendre le problème et trouver une solution appropriée.

Conclusion

Je suis sûr que vous conviendrez que l'extraction asynchrone est étonnamment facile à utiliser. Les tâches lourdes sont effectuées par Core Data, ce qui signifie qu'il n'est pas nécessaire de fusionner manuellement les résultats de la demande d'extraction asynchrone avec le contexte de l'objet géré. Votre seul travail consiste à mettre à jour l'interface utilisateur lorsque la demande d'extraction asynchrone vous transmet ses résultats. Avec les mises à jour par lots, c'est un excellent ajout au framework Core Data.

Cet article conclut également cette série sur les données de base. Vous avez beaucoup appris sur le framework Core Data et vous connaissez tous les éléments essentiels pour utiliser Core Data dans une application réelle. Core Data est un framework puissant et, avec la sortie d'iOS 8, Apple nous a montré qu'il s'améliorait chaque année..