web-dev-qa-db-fra.com

Comment traiter les instances temporaires NSManagedObject?

J'ai besoin de créer des instances NSManagedObject, de faire des trucs avec elles, puis de les jeter ou de les stocker dans sqlite db. Le problème est que je ne peux pas créer d'instances de NSManagedObject sans lien avec NSManagedObjectContext et cela signifie que je dois éclaircir quelque part après avoir décidé que je n'ai pas besoin de certains 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 le commun aux deux magasins contexte? Ou dois-je créer des contextes séparés pour une telle tâche?


UPD:

Maintenant, je songe à créer un contexte séparé pour le magasin 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 à partir du graphe d'objets, l'intégralité du graphe est-elle également insérée dans le contexte?

83
fspirit

NOTE: 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 instances NSManagedObject non associées. Ma recommandation actuelle consiste à utiliser des instances enfants temporaires NSManagedObjectContext.

Réponse originale

Le moyen le plus simple de procéder consiste à créer vos instances NSManagedObject sans un NSManagedObjectContext associé.

NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

Puis quand vous voulez le sauvegarder:

[myMOC insertObject:unassociatedObject];
NSError *error = nil;
if (![myMoc save:&error]) {
  //Respond to the error
}
143
Marcus S. Zarra

iOS5 offre une alternative plus simple à la réponse de Mike Weller. Utilisez plutôt un child NSManagedObjectContext. 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 ignorer les modifications, n'enregistrez tout simplement 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 ce problème, utilisez objectID, pour obtenir l'objet du contexte enfant. par exemple.

NSManagedObjectID *mid = [myManagedObject objectID];
MyManagedObject *mySafeManagedObject = [childContext objectWithID:mid];
object.relationship=mySafeManagedObject;

Remarque: 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.

37
railwayparade

La création d'objets temporaires à partir d'un contexte nil fonctionne correctement jusqu'à ce que vous essayiez réellement d'avoir une relation avec un objet dont le contexte! = Nil!

assurez-vous que ça vous va.

9
user134611

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, les mute, etc.

Lorsque vient le moment de sauvegarder, vous devez appeler [tempContext save: ...] sur le 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 les replacer 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 aussi la façon dont vous devez gérer les opérations de données de base multithreads. Un contexte par thread.

Si vous devez 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 une NSManagedObject dans le mauvais contexte, vous obtiendrez des exceptions lors de la sauvegarde.

9
Mike Weller

Ce que vous décrivez correspond exactement à ce qu'un NSManagedObjectContextis.

De Guide de programmation de base de données: notions de base de base de données

Vous pouvez considérer le contexte d'un objet géré comme un bloc-notes intelligent. Lorsque vous récupérez des objets d'un magasin persistant, vous apportez des copies temporaires sur le bloc-notes où ils forment un graphe d'objet (ou une collection de graphes d'objet). Vous pouvez ensuite modifier ces objets comme bon vous semble. Toutefois, sauf si vous enregistrez réellement ces modifications, le magasin persistant reste inchangé.

And Guide de programmation Core Data: Validation d’objet géré

Cela sous-tend également l’idée d’un contexte d’objet géré représentant un «bloc-notes». En général, vous pouvez importer des objets gérés sur le bloc-notes et les éditer comme vous le souhaitez avant de valider ou de supprimer les modifications.

NSManagedObjectContexts sont conçus pour être légers. 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 impliquerait de définir le même coordinateur de magasin persistant dans chaque contexte. Aujourd'hui, cela signifie 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'une NSManagedObjectContext est source de problèmes. N'oubliez pas que les données de base sont en définitive un mécanisme de suivi des modifications pour un graphe d'objets. Pour cette raison, les objets gérés sont vraiment partie du contexte de l'objet géré } _. Le contexte observe leur cycle de vie }, et sans le contexte toutes les fonctionnalités de l'objet géré ne fonctionneront pas correctement.

8
quellish

Selon l’utilisation que vous faites de l’objet temporaire, certaines recommandations s’appliquent. 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 des relations avec un ou plusieurs objets existants et 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'un tableau et la logique pour ce faire est moins élégante.)

Les options pour les objets temporaires sont les suivantes:

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 dans le contexte enfant. (Je n'ai trouvé aucune documentation qui indique le contraire alors je dois assumer.)

2) Créez l'objet temporaire avec le contexte d'objet nil .Cela ne fonctionne pas et entraîne la perte/la corruption de données.

Ma solution: J'ai résolu ce problème en créant l'objet temporaire avec un contexte d'objet nil, mais lorsque je sauvegarde l'objet, plutôt que de l'insérer en tant que # 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: elle me permet de copier facilement des attributs et des relations pour tout objet.

6
greg

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!

Nous devons également ne pas oublier de le libérer

[unassociatedObject release]
1
Lucas

Je suis en train de réécrire cette réponse pour Swift comme toutes les questions similaires pour Swift rediriger 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 sauvegarder l'objet, vous pouvez l'insérer dans le contexte et le sauvegarder.

myContext.insert(unassociatedObject)
// Saving the object
do {
    try self.stack.saveContext()
    } catch {
        print("save unsuccessful")
    }
}
0
Mitul Jindal