Je dois créer des instances NSManagedObject
, faire des trucs avec eux, puis les jeter ou les stocker dans sqlite db. Le problème est que je ne peux pas créer des instances de NSManagedObject
non connectées à NSManagedObjectContext
, ce qui signifie que je dois me débrouiller d’une manière ou d’une autre après avoir décidé que je n’ai pas besoin de certains objects de ma firebase database.
Pour y faire face, j’ai créé un magasin en mémoire utilisant le même coordinateur et j’y place des objects temporaires en utilisant assignObject:toPersistentStore.
Maintenant, comment puis-je m’assurer que ces objects temporaires n’atteignent pas les données, que j’extrais du contexte commun aux deux magasins? Ou dois-je créer des contextes distincts pour une telle tâche?
Maintenant, je pense à créer un contexte distinct pour le stockage en mémoire. Comment déplacer des objects d’un contexte à un autre? Juste en utilisant [context insertObject:]? Cela fonctionnera-t-il correctement dans cette configuration? Si j’insère un object du graphique d’objects, le graphique entier est-il également inséré dans le contexte?
La méthode la plus simple consiste à créer vos instances NSManagedObject
sans NSManagedObjectContext
associé.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC]; NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
Ensuite, quand vous voulez l’enregistrer:
[myMOC insertObject:unassociatedObject]; NSError *error = nil; if (![myMoc save:&error]) { //Respond to the error }
iOS5 fournit une alternative plus simple à la réponse de Mike Weller. Utilisez plutôt un enfant NSManagedObjectContext. Il élimine le besoin de faire du trampoline via NSNotificationCenter
Pour créer un contexte enfant:
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; childContext.parentContext = myMangedObjectContext;
Créez ensuite vos objects en utilisant le contexte enfant:
NSManagedObject *o = [NSEntityDescription insertNewObjectForEntityForName:@"MyObject" inManagedObjectContext:childContext];
Les modifications ne sont appliquées que lorsque le contexte enfant est enregistré. Donc, pour ignorer les modifications, il suffit de ne pas enregistrer.
Il y a toujours une limite aux relations. C’est-à-dire que vous ne pouvez pas créer de relations avec des objects dans d’autres contextes. Pour contourner cela, utilisez objectID, pour obtenir l’object du contexte enfant. par exemple.
NSManagedObjectID *mid = [myManagedObject objectID]; MyManagedObject *mySafeManagedObject = [childContext objectWithID:mid]; object.relationship=mySafeManagedObject;
Notez que l’enregistrement du contexte enfant applique les modifications au contexte parent. L’enregistrement du contexte parent conserve les modifications.
Voir wwdc 2012 session 214 pour une explication complète.
La manière correcte de réaliser ce genre de chose est d’utiliser un nouveau contexte d’object géré. Vous créez un contexte d’object géré avec le même stockage persistant:
NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease]; [tempContext setPersistentStore:[originalContext persistentStore]];
Ensuite, vous ajoutez de nouveaux objects, vous les modifiez, etc.
Au moment de sauvegarder, vous devez appeler [tempContext save: …] sur tempContext et gérer la notification de sauvegarde pour la fusionner dans votre contexte d’origine. Pour rejeter les objects, relâchez simplement ce contexte temporaire et oubliez-le.
Ainsi, lorsque vous enregistrez le contexte temporaire, les modifications sont conservées dans le magasin et il vous suffit de récupérer ces modifications dans votre contexte principal:
/* Called when the temp context is saved */ - (void)tempContextSaved:(NSNotification *)notification { /* Merge the changes into the original managed object context */ [originalContext mergeChangesFromContextDidSaveNotification:notification]; } // Here's where we do the save itself // Add the notification handler [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tempContextSaved:) name:NSManagedObjectContextDidSaveNotification object:tempContext]; // Save [tempContext save:NULL]; // Remove the handler again [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:tempContext];
C’est également ainsi que vous devez gérer les opérations de données de base multithread. Un contexte par fil.
Si vous devez accéder à des objects existants à partir de ce contexte temporaire (pour append des relations, etc.), vous devez utiliser l’ID de l’object pour obtenir une nouvelle instance comme celle-ci:
NSManagedObject *objectInOriginalContext = ...; NSManagedObject *objectInTemporaryContext = [tempContext objectWithID:[objectInOriginalContext objectID]];
Si vous essayez d’utiliser un NSManagedObject
dans le mauvais contexte, vous obtiendrez des exceptions lors de l’enregistrement.
Créer des objects temporaires à partir d’un contexte nul fonctionne correctement jusqu’à ce que vous essayiez réellement d’établir une relation avec un object dont le contexte! = Nil!
assurez-vous que cela vous convient.
Ce que vous décrivez est exactement ce qu’est un NSManagedObjectContext
.
Guide de programmation de firebase database: bases des données de base
Vous pouvez considérer un contexte d’object géré comme un bloc-notes intelligent. Lorsque vous récupérez des objects à partir d’un magasin persistant, vous apportez des copies temporaires sur le bloc-notes où ils forment un graphique d’object (ou une collection de graphiques d’object). Vous pouvez ensuite modifier ces objects comme vous le souhaitez. Sauf si vous enregistrez réellement ces modifications, le stockage persistant rest inchangé.
Et guide de programmation de données de base: validation d’objects gérés
Cela sous-tend également l’idée d’un contexte d’object géré représentant un “bloc-notes”. En général, vous pouvez importer des objects gérés sur le bloc-notes et les modifier comme vous le souhaitez avant de les modifier ou de les supprimer.
NSManagedObjectContext
est conçu pour être léger. Vous pouvez les créer et les supprimer à volonté – c’est le coordinateur des magasins persistants et ses dépendances qui sont “lourdes”. Un seul coordinateur de magasin persistant peut être associé à de nombreux contextes. Sous l’ancien modèle de confinement de threads obsolète, cela signifierait de définir le même coordinateur de stockage persistant sur chaque contexte. Aujourd’hui, cela impliquerait de connecter des contextes nesteds à un contexte racine associé au coordinateur du magasin persistant.
Créez un contexte, créez et modifiez des objects gérés dans ce contexte. Si vous souhaitez les conserver et communiquer ces modifications, enregistrez le contexte. Sinon, jetez-le.
Tenter de créer des objects gérés indépendamment de NSManagedObjectContext
demande des problèmes. Rappelez-vous que Core Data est finalement un mécanisme de suivi des modifications pour un graphe d’object. De ce fait, les objects gérés font réellement partie du contexte de l’object géré . Le contexte observe leur cycle de vie et, sans le contexte, toutes les fonctionnalités de l’object géré ne fonctionnent pas correctement.
Selon votre utilisation de l’object temporaire, les recommandations ci-dessus sont sujettes à caution. Mon cas d’utilisation est que je veux créer un object temporaire et le lier aux vues. Lorsque l’utilisateur choisit d’enregistrer cet object, je souhaite configurer des relations avec des objects existants et les enregistrer. Je veux faire cela pour éviter de créer un object temporaire contenant ces valeurs. (Oui, il suffit d’attendre que l’utilisateur enregistre et récupère le contenu de la vue, mais je place ces vues dans un tableau et la logique pour le faire est moins élégante.)
Les options pour les objects temporaires sont les suivantes:
1) (Préféré) Créez l’object temporaire dans un contexte enfant. Cela ne fonctionnera pas car je lie l’object à l’interface utilisateur et je ne peux pas garantir que les accesseurs d’object sont appelés sur le contexte enfant. (Je n’ai trouvé aucune documentation qui indique le contraire, donc je dois assumer.)
2) Créez l’object temporaire avec un contexte d’object nul. Cela ne fonctionne pas et entraîne une perte / corruption des données.
Ma solution: j’ai résolu ce problème en créant l’object temporaire avec un contexte d’object nul, mais lorsque je sauvegarde l’object, plutôt que de l’insérer en tant que # 2, je copie tous ses atsortingbuts dans un nouvel object que je crée dans le contexte principal. J’ai créé une méthode de prise en charge dans ma sous-classe NSManagedObject appelée cloneInto: cela me permet de copier facilement des atsortingbuts et des relations pour n’importe quel object.
Pour moi, la réponse de Marcus n’a pas fonctionné. Voici ce qui a fonctionné pour moi:
NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC]; NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
alors, si je décide de le sauvegarder:
[myMOC insertObject:unassociatedObjet]; NSError *error = nil; [myMoc save:&error]; //Check the error!
Il ne faut pas non plus oublier de le libérer
[unassociatedObject release]
Je réécris cette réponse pour Swift comme toutes les questions similaires pour une réorientation rapide vers cette question.
Vous pouvez déclarer l’object sans ManagedContext en utilisant le code suivant.
let entity = NSEntityDescription.entity(forEntityName: "EntityName", in: myContext) let unassociatedObject = NSManagedObject.init(entity: entity!, insertInto: nil)
Plus tard, pour enregistrer l’object, vous pouvez l’insérer dans le contexte et l’enregistrer.
myContext.insert(unassociatedObject) // Saving the object do { try self.stack.saveContext() } catch { print("save unsuccessful") } }