J'ai besoin de créer des NSManagedObject
instances, de faire des choses avec elles, puis de les détruire ou de les stocker dans sqlite db. Le problème est que je ne peux pas créer d'instances de NSManagedObject
non connecté à NSManagedObjectContext
et cela signifie que je dois clarifier d'une manière ou d'une autre après avoir décidé que je n'ai pas besoin de certains des objets de ma base de données.
Pour y faire face, j'ai créé un magasin en mémoire en utilisant le même coordinateur et j'y place des objets temporaires en utilisant assignObject:toPersistentStore.
Maintenant, comment puis-je m'assurer que ces objets temporaires n'atteignent pas les données, que je récupère dans le commun au contexte des deux magasins? Ou dois-je créer des contextes séparés pour une telle tâche?
UPD:
Maintenant, je pense à créer un contexte séparé pour le stockage en mémoire. Comment déplacer des objets d'un contexte à un autre? Juste en utilisant [context insertObject:]? Cela fonctionnera-t-il correctement dans cette configuration? Si j'insère un objet du graphe d'objets, le graphe entier est-il également inséré dans le contexte?
la source
Réponses:
REMARQUE: cette réponse est très ancienne. Voir les commentaires pour l'historique complet. Ma recommandation a depuis changé et je ne recommande plus d'utiliser des
NSManagedObject
instances non associées . Ma recommandation actuelle est d'utiliser desNSManagedObjectContext
instances enfants temporaires .Réponse originale
Le moyen le plus simple de le faire est de créer vos
NSManagedObject
instances sans associéNSManagedObjectContext
.NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC]; NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
Ensuite, lorsque vous souhaitez le sauvegarder:
[myMOC insertObject:unassociatedObject]; NSError *error = nil; if (![myMoc save:&error]) { //Respond to the error }
la source
iOS5 offre une alternative plus simple à la réponse de Mike Weller. À la place, utilisez un NSManagedObjectContext enfant . Il supprime le besoin de trampoline via NSNotificationCenter
Pour créer un contexte enfant:
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; childContext.parentContext = myMangedObjectContext;
Créez ensuite vos objets 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 annuler les modifications, ne sauvegardez pas.
Il y a toujours une limitation sur les relations. c'est-à-dire que vous ne pouvez pas créer de relations avec des objets dans d'autres contextes. Pour contourner cela, utilisez objectID, pour obtenir l'objet 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 persiste les modifications.
Voir wwdc 2012 session 214 pour une explication complète.
la source
moc
dans le troisième extrait? Est-cechildContext
oumyMangedObjectContext
?NSManagedObject
fournit déjà le pertinentNSManagedObjectContext
, vous pouvez automatiser le choix du contexte:NSManagedObject* objectRelatedContextually = [objectWithRelationship.managedObjectContext objectWithID:objectRelated.objectID];
et puisobjectWithRelationship.relationship = objectRelatedContextually;
.La bonne façon de réaliser ce genre de chose est d'utiliser un nouveau contexte d'objet géré. Vous créez un contexte d'objet géré avec le même magasin persistant:
NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease]; [tempContext setPersistentStore:[originalContext persistentStore]];
Ensuite, vous ajoutez de nouveaux objets, vous les mutez, etc.
Quand vient le temps 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 supprimer les objets, libérez 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 multi-thread. Un contexte par thread.
Si vous avez besoin d'accéder à des objets existants à partir de ce contexte temporaire (pour ajouter des relations, etc.), vous devez utiliser l'ID de l'objet 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.la source
NSManagedObjectContext
coûteux car le fait de tenir debout coûte cher en mémoire et en CPU. Je me rends compte que cela figurait à l'origine dans certains des exemples Apple, mais ils ont mis à jour et corrigé ces exemples.La création d'objets temporaires à partir d'un contexte nul fonctionne bien jusqu'à ce que vous essayiez réellement d'avoir une relation avec un objet dont le contexte! = Nil!
assurez-vous que vous êtes d'accord avec cela.
la source
Ce que vous décrivez est exactement à quoi
NSManagedObjectContext
sert un .Tiré du Guide de programmation des données de base: Principes de base des données de base
Et Guide de programmation des données de base: validation d'objets gérés
NSManagedObjectContext
s sont conçus pour être légers. Vous pouvez les créer et les supprimer à volonté - c'est le coordinateur des magasins persistants et ce sont les dépendances qui sont "lourdes". Un seul coordinateur de magasin persistant peut être associé à de nombreux contextes. Dans l'ancien modèle de confinement des threads obsolète, cela signifierait définir le même coordinateur de magasin persistant sur chaque contexte. Aujourd'hui, cela signifierait connecter des contextes imbriqués à un contexte racine associé au coordinateur de magasin persistant.Créez un contexte, créez et modifiez des objets 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 objets gérés indépendamment d'un
NSManagedObjectContext
pose problème. N'oubliez pas que Core Data est en fin de compte un mécanisme de suivi des modifications pour un graphique d'objets. Pour cette raison, les objets gérés font vraiment partie du contexte des objets gérés . Le contexte observe leur cycle de vie et sans le contexte, toutes les fonctionnalités des objets gérés ne fonctionneront pas correctement.la source
En fonction de votre utilisation de l'objet temporaire, il y a quelques mises en garde aux recommandations ci-dessus. Mon cas d'utilisation est que je souhaite créer un objet temporaire et le lier à des vues. Lorsque l'utilisateur choisit d'enregistrer cet objet, je souhaite configurer les relations avec les objets existants et les enregistrer. Je veux faire cela pour éviter de créer un objet temporaire pour contenir ces valeurs. (Oui, je pourrais simplement attendre que l'utilisateur enregistre, puis récupère le contenu de la vue, mais je mets ces vues à l'intérieur d'une table et la logique pour le faire est moins élégante.)
Les options pour les objets temporaires sont:
1) (Préféré) Créez l'objet temporaire dans un contexte enfant. Cela ne fonctionnera pas car je lie l'objet à l'interface utilisateur et je ne peux pas garantir que les accesseurs d'objet sont appelés sur le contexte enfant. (Je n'ai trouvé aucune documentation indiquant le contraire, je dois donc supposer.)
2) Créez l'objet temporaire avec un contexte d'objet nul. Cela ne fonctionne pas et entraîne une perte / corruption de données.
Ma solution: J'ai résolu ce problème en créant l'objet temporaire avec un contexte d'objet nul, mais lorsque j'enregistre l'objet, plutôt que de l'insérer en tant que n ° 2, je copie tous ses attributs dans un nouvel objet 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: qui me permet de copier facilement les attributs et les relations pour n'importe quel objet.
la source
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];
puis, 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 publier
la source
Je réécris cette réponse pour Swift comme toutes les questions similaires pour une redirection rapide vers cette question.
Vous pouvez déclarer l'objet sans ManagedContext à l'aide du code suivant.
let entity = NSEntityDescription.entity(forEntityName: "EntityName", in: myContext) let unassociatedObject = NSManagedObject.init(entity: entity!, insertInto: nil)
Plus tard, pour enregistrer l'objet, 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") } }
la source