web-dev-qa-db-fra.com

Existe-t-il un moyen d'instancier un NSManagedObject sans l'insérer?

J'ai une interface utilisateur pour insérer une transaction. une fois que l'utilisateur clique sur un plus, il obtient l'écran et je souhaite instancier mon entité Core Data NSManagedObject laisser l'utilisateur travailler dessus. Ensuite, lorsque l'utilisateur clique sur le bouton Enregistrer, j'appelle la fonction d'enregistrement.

donc au code:

transaction = (Transaction *)[NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:self.managedObjectContext];
//even if i dont call save: its going to show up on my table
[self.managedObjectContext save:&error]

P.S J'utilise un NSFetchedResultsController sur cette table et je vois que le NSFetchedResultsController insère une section et un objet dans la table.

Je pense que s'il existe un moyen d'instancier la transaction NSManagedObject, je pourrais le mettre à jour sans enregistrer jusqu'à ce que le client choisisse de le faire.

54
Edward Ashak

Il y a un problème fondamental avec l'utilisation d'un MOC nul: les objets dans différents MOC ne sont pas censés se référencer les uns les autres - cela s'applique probablement aussi lorsqu'un côté d'une relation a un MOC nul. Que se passe-t-il si vous enregistrez? (Que se passe-t-il lorsqu'une autre partie de votre application enregistre?)

Si votre objet n'a pas de relations, alors vous pouvez faire beaucoup de choses (comme NSCoding).

Vous pourrez peut-être utiliser -[NSManagedObject isInserted] dans NSPredicate (vraisemblablement c'est OUI entre l'insertion et la sauvegarde réussie). Vous pouvez également utiliser une propriété transitoire avec le même comportement (définissez-la sur YES dans awakeFromInsert et NO dans willSave). Ces deux éléments peuvent être problématiques si une autre partie de votre application est enregistrée.

Cependant, l'utilisation d'un deuxième MOC est la façon dont CoreData est "censé" être utilisé; il gère automatiquement la détection et la résolution des conflits. Bien sûr, vous ne voulez pas créer un nouveau MOC chaque fois qu'il y a un changement; il peut être vaguement judicieux d'avoir un MOC pour les modifications non enregistrées par le lent "thread utilisateur" si cela ne vous dérange pas que certaines parties de l'interface utilisateur voient des changements non enregistrés dans d'autres parties (la surcharge de communication inter-MOC est négligeable).

17
tc.

Pour ce que ça vaut, Marcus Zarra semble promouvoir l'approche contextuelle nil, affirmant qu'il est coûteux de créer un nouveau contexte. Pour plus de détails, voir cette réponse à une question similaire.

Mise à jour

J'utilise actuellement l'approche du contexte nul et j'ai rencontré quelque chose qui pourrait intéresser les autres. Pour créer un objet géré sans contexte, vous utilisez le initWithEntity:insertIntoManagedObjectContext: méthode de NSManagedObject. Selon la documentation d'Apple pour cette méthode:

Si context n'est pas nil, cette méthode appelle [context insertObject:self] (ce qui provoque l'appel de awakeFromInsert).

L'implication ici est importante. L'utilisation d'un contexte nil lors de la création d'un objet géré empêchera insertObject: d'être appelé et donc d'empêcher awakeFromInsert d'être appelé. Par conséquent, toute initialisation d'objet ou définition de valeurs de propriété par défaut effectuée dans awakeFromInsert ne se produira pas automatiquement lors de l'utilisation d'un contexte nil.

Conclusion: lorsque vous utilisez un objet géré sans contexte, awakeFromInsert ne sera pas appelé automatiquement et vous devrez peut-être du code supplémentaire pour compenser.

37
James Huddleston

voici comment j'ai travaillé:

En charge, lorsque nous savons que nous avons affaire à une nouvelle transaction, j'ai créé une transaction hors contexte.

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Transaction" inManagedObjectContext:self.managedObjectContext];
        transaction = (Transaction *)[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

puis quand il s'agissait d'établir un navire relationnel, j'ai fait ceci:

if( transaction.managedObjectContext == nil){
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Category" inManagedObjectContext:self.managedObjectContext];
        Category *category = (Category *)[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
        category.title = ((Category *)obj).title;
        transaction.category = category;
        [category release];
    }
    else {
        transaction.category = (Category *)obj;
    }

et à la fin pour économiser:

if (transaction.managedObjectContext == nil) {
        [self.managedObjectContext insertObject:transaction.category];
        [self.managedObjectContext insertObject:transaction];
    }
    //NSLog(@"\n saving transaction\n%@", self.transaction);

    NSError *error;
    if (![self.managedObjectContext save:&error]) {
        // Update to handle the error appropriately.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }
19
Edward Ashak

Vous pouvez insérer un NSManagedObjectContext avec le -[NSManagedObject initWithEntity:insertIntoManagedObjectContext:], en passant nil pour le contexte d'objet géré. Vous devez bien sûr l'assigner à un contexte (en utilisant -[NSManageObjectContext insertObject:] avant d'enregistrer. Pour autant que je sache, ce n'est pas vraiment le modèle prévu dans Core Data (mais voir la réponse de @ mzarra ici ). Il existe des problèmes de commande délicats (c'est-à-dire s'assurer que l'instance est affectée à un contexte avant qu'elle ne s'attende à en avoir un, etc.). Le modèle plus standard consiste à créer un nouveau contexte d'objet géré et à insérer votre nouvel objet dans ce contexte. Lorsque l'utilisateur enregistre, enregistrez le contexte et gérez le NSManagedObjectDidSaveNotification pour fusionner les modifications dans votre contexte "principal". Si l'utilisateur annule la transaction, il vous suffit de faire sauter le contexte et de poursuivre votre activité.

8
Barry Wark

Un NSManagedObject peut être créé en utilisant le nil comme contexte, mais s'il y a d'autres NSManagedObjects qu'il doit lier, cela entraînera une erreur. La façon dont je le fais, je passe le contexte dans l'écran de destination et crée un NSManagedObject dans cet écran. Effectuer toutes les modifications relient d'autres NSManagedObjects. Si l'utilisateur appuie sur le bouton Annuler, je supprime NSManagedObject et enregistre le contexte. Si l'utilisateur appuie sur le bouton Enregistrer, je mets à jour les données dans NSManagedObject, enregistrez-les dans le contexte et relâchez l'écran. Dans l'écran source, je mets à jour la table avec un rechargement.

La suppression de NSManagedObject dans l'écran de destination donne aux données centrales le temps de mettre à jour le fichier. C'est généralement assez de temps pour que vous ne puissiez pas voir le changement dans la vue de table. Dans l'application iPhone Calendar, vous avez un délai entre le moment où il enregistre et celui où il apparaît dans la vue de table. Cela pourrait être considéré comme une bonne chose du point de vue de l'interface utilisateur que votre utilisateur se concentre sur la ligne qui vient d'être ajoutée. J'espère que ça aide.

2
ejkujan