Il existe une règle d'or en ce qui concerne les données de base: un contexte d'objet géré par thread. Les contextes d'objets gérés ne sont pas thread-safe, donc si vous travaillez dans une tâche d'arrière-plan, utilisez le thread principal pour éviter les conflits de threads avec les opérations de l'interface utilisateur ou créez un nouveau contexte pour effectuer le travail. quelques secondes alors vous devriez faire le dernier pour empêcher votre UI de se bloquer.
Pour ce faire, vous créez un nouveau contexte et lui donner le même magasin persistant que votre contexte principal:
NSManagedObjectContext *backgroundContext = [[[NSManagedObjectContext alloc] init] autorelease];
[backgroundContext setPersistentStoreCoordinator:[mainContext persistentStoreCoordinator]];
Faites ce que les opérations que vous devez faire, lorsque vous enregistrez ce nouveau contexte que vous devez gérer la notification de sauvegarde et fusionner les modifications dans votre contexte principal avec le message mergeChangesFromContextDidSaveNotification:
. Le code devrait ressembler à ceci:
/* Save notification handler for the background context */
- (void)backgroundContextDidSave:(NSNotification *)notification {
/* Make sure we're on the main thread when updating the main context */
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
withObject:notification
waitUntilDone:NO];
return;
}
/* merge in the changes to the main context */
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}
/* ... */
/* Save the background context and handle the save notification */
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:backgroundContext];
[backgroundContext save:NULL];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:syncContext];
Manipulation la sauvegarde notifcation et la fusion est importante, sinon votre principale interface utilisateur/contexte ne verra pas les modifications apportées. En fusionnant, votre principal fetchResultsController etc. obtiendra des événements de changement et mettra à jour votre interface utilisateur comme vous vous y attendez.
Une autre chose importante à noter est que les instances de NSManagedObject ne peuvent être utilisées que dans le contexte d'où elles ont été extraites. Si votre opération nécessite une référence à un objet, vous devez passer le objectID
de l'objet à l'opération et extraire une nouvelle instance NSManagedObject du nouveau contexte à l'aide de existingObjectWithID:
. Donc, quelque chose comme:
/* This can only be used in operations on the main context */
MyNSManagedObject *objectInMainContext =
[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
/* This can now be used in your background context */
MyNSManagedObject *objectInBackgroundContext =
(MyNSManagedObject *) [backgroundContext existingObjectWithID:[objectInMainContext objectID]];
Donc, ce que vous dites est que dans mon cas, au lieu d'utiliser fetchedResultsController, je devrais créer un nouveau contexte d'objet géré (backgro undManagedObjectContext), récupère l'objet géré requis, effectue les opérations requises, met à jour l'objet géré, enregistre ce contexte d'objet géré (backgroundManagedObjectContext), puis fusionne les modifications pour refléter dans le contexte de l'objet géré principal. Cela rendra ma vie sacrément misérable. – Mustafa
Vous pouvez toujours utiliser le contrôleur de résultats récupérés pour afficher et mettre à jour des éléments dans votre interface utilisateur. Le code que je vous ai montré est tout ce qui est nécessaire pour effectuer toutes les opérations dont vous avez besoin dans un contexte séparé, rien d'autre ne devrait changer. –
Il existe des moyens de contourner cette règle d'or: https://github.com/adam-roth/coredata-threadsafe. – aroth