Question: Comment puis-je obtenir mon enfant en contexte pour voir les changements ont persisté sur le contexte parent, de sorte qu'ils déclenchent mon NSFetchedResultsController de mettre à jour l'INTERFACE utilisateur?
Voici la configuration:
Vous avez une application qui télécharge et ajoute beaucoup de données XML (environ 2 millions d'enregistrements, chacun à peu près la taille d'un paragraphe de texte).fichier sqlite devient environ 500 MO en taille. L'ajout de ce contenu dans la Base de Données prend du temps, mais vous voulez que l'utilisateur soit en mesure d'utiliser l'application pendant les chargements de données dans la banque de données de manière incrémentale. Il faut qu'il soit invisible et imperceptible pour l'utilisateur que de grandes quantités de données sont déplacés, donc pas d'accroche, pas de nervosité: défile comme dans du beurre. Encore, l'application est plus utile, le plus de données est ajouté à cela, nous ne pouvons pas attendre une éternité pour que les données soient ajoutés à la Base de Données du magasin. Dans le code, ce qui signifie que je voudrais vraiment éviter ce type de code dans le code d'importation:
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
L'application est iOS 5 uniquement si l'unité la plus lente qu'il doit soutenir un iPhone 3GS.
Voici les ressources que j'ai utilisé jusqu'à présent pour développer ma solution actuelle:
Apple de Base de Données Guide de Programmation: Efficacement l'Importation de Données
- Utilisation Autorelease Piscines pour garder la mémoire vers le bas
- Les Relations De Coût. À l'importation, à plat, puis le patch des relations à la fin
- Ne demande pas si vous pouvez l'aider, il ralentit les choses dans un temps O(n^2) manière
- L'importation par Lots: enregistrer, de réinitialisation, les égoutter et répéter
- Désactiver l'Annulation du Gestionnaire à l'importation
iDeveloper TV - Base de Données de Performances
- 3 Contextes: Maître Principal et le Confinement types de contexte
iDeveloper TV - Base de Données pour Mac, iPhone et iPad mise à Jour
- L'exécution d'économiser sur d'autres files d'attente avec performBlock rend les choses rapidement.
- Cryptage ralentit les choses, de le désactiver si vous le pouvez.
L'importation et l'Affichage de Grands Ensembles de Données dans la Base de Données par Marcus Zarra
- Vous pouvez ralentir l'importation en donnant du temps à l'exécution de la boucle, donc, les choses se sentir lisse à l'utilisateur.
- Exemple de Code prouve qu'il est possible de faire de grandes importations et de garder l'INTERFACE utilisateur réactive, mais pas aussi rapide qu'avec 3 contextes et async l'enregistrer sur le disque.
Ma Solution Actuelle
J'ai eu 3 cas de NSManagedObjectContext:
masterManagedObjectContext - C'est le contexte qui est le NSPersistentStoreCoordinator et est responsable de la sauvegarde sur le disque. Je le fais donc ma sauve peut être asynchrone et donc très rapide. J'ai créer sur lancer comme ceci:
masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
mainManagedObjectContext - C'est le contexte de l'INTERFACE utilisateur utilise partout. C'est un enfant de la masterManagedObjectContext. J'ai créer comme ceci:
mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[mainManagedObjectContext setUndoManager:nil];
[mainManagedObjectContext setParentContext:masterManagedObjectContext];
backgroundContext - Ce contexte est créé dans mon NSOperation sous-classe qui est responsable de l'importation de données XML dans la Base de Données. J'ai créer dans le fonctionnement de la méthode principale et lien vers le maître de la situation.
backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[backgroundContext setUndoManager:nil];
[backgroundContext setParentContext:masterManagedObjectContext];
Cela fonctionne très, TRÈS vite. Tout simplement en faisant ce 3 contexte de l'installation, j'ai pu améliorer ma vitesse de l'importation de plus de 10 fois! Honnêtement, c'est difficile à croire. (Cette conception de base devrait faire partie de la norme de Base du modèle de Données...)
Pendant le processus d'importation-je économiser de 2 façons différentes. Tous les 1000 articles que j'ai enregistrer sur le contexte:
BOOL saveSuccess = [backgroundContext save:&error];
Ensuite, à la fin du processus d'importation, je l'ai enregistrer sur le maître/contexte parent qui, en apparence, pousse des modifications à l'autre enfant de contextes, y compris le contexte principal:
[masterManagedObjectContext performBlock:^{
NSError *parentContextError = nil;
BOOL parentContextSaveSuccess = [masterManagedObjectContext save:&parentContextError];
}];
Problème: Le problème est que mon INTERFACE ne sera pas de mise à jour jusqu'à ce que je recharger la vue.
J'ai une simple UIViewController avec un UITableView qui est de la fed de données à l'aide d'un NSFetchedResultsController. Lorsque le processus d'Importation est terminé, l'NSFetchedResultsController vois pas de changements de la part du parent/master contexte et pour que l'INTERFACE utilisateur n'est pas automatiquement mise à jour, comme je suis habitué à voir. Si je pop le UIViewController hors de la pile et de le charger à nouveau toutes les données sont là.
Question: Comment puis-je obtenir mon enfant en contexte pour voir les changements ont persisté sur le contexte parent, de sorte qu'ils déclenchent mon NSFetchedResultsController de mettre à jour l'INTERFACE utilisateur?
J'ai essayé ce qui suit, qui se contente d'être l'application:
- (void)saveMasterContext {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:masterManagedObjectContext];
NSError *error = nil;
BOOL saveSuccess = [masterManagedObjectContext save:&error];
[notificationCenter removeObserver:self name:NSManagedObjectContextDidSaveNotification object:masterManagedObjectContext];
}
- (void)contextChanged:(NSNotification*)notification
{
if ([notification object] == mainManagedObjectContext) return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
return;
}
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}