J'ai ce que je suppose être une configuration assez standard, avec un bloc-notes MOC qui n'est jamais enregistré (contenant un tas d'objets téléchargés sur le Web) et un autre MOC permanent qui persiste. Lorsque l'utilisateur sélectionne un objet de scratchMOC pour l'ajouter à sa bibliothèque, je veux soit 1) supprimer l'objet de scratchMOC et l'insérer dans permanentMOC, soit 2) copier l'objet dans permanentMOC. Le Core Data FAQ dit que je peux copier un objet comme celui-ci:
NSManagedObjectID *objectID = [managedObject objectID];
NSManagedObject *copy = [context2 objectWithID:objectID];
(Dans ce cas, context2 serait permanentMOC.) Cependant, lorsque je fais cela, l'objet copié est défectueux; les données sont initialement non résolues. Lorsqu'il est résolu, plus tard, toutes les valeurs sont nulles; aucune des données (attributs ou relations) du managedObject d'origine n'est réellement copiée ou référencée. Par conséquent, je ne vois aucune différence entre l'utilisation de cette méthode objectWithID: et l'insertion d'un objet entièrement nouveau dans permanentMOC à l'aide de insertNewObjectForEntityForName :.
Je me rends compte que je peux créer un nouvel objet dans permanentMOC et copier manuellement chaque paire clé-valeur de l'ancien objet, mais je ne suis pas très satisfait de cette solution. (J'ai un certain nombre d'objets gérés différents pour lesquels j'ai ce problème, donc je ne veux pas avoir à écrire et mettre à jour la copie: des méthodes pour chacun d'eux pendant que je continue de développer.) Existe-t-il une meilleure façon?
Tout d'abord, avoir plus d'un NSManagedObjectContext
sur un seul thread n'est pas pas une configuration standard. 99% du temps, vous n'avez besoin que d'un seul contexte et cela résoudra cette situation pour vous.
Pourquoi pensez-vous avoir besoin de plus d'un NSManagedObjectContext
?
C'est en fait l'un des rares cas d'utilisation que j'ai vu où cela a du sens. Pour ce faire, vous devez effectuer une copie récursive de l'objet d'un contexte à l'autre. Le workflow serait le suivant:
-dictionaryWithValuesForKeys
et -[NSEntityDescription attributesByName]
pour faire ça.-setValuesForKeysWithDictionary
)-[NSEntityDescription relationshipsByName]
Comme mentionné par un autre, vous pouvez télécharger l'exemple de code de mon livre à partir de The Pragmatic Programmers Core Data Book et voir une solution à ce problème. Bien sûr, dans le livre, j'en parle plus en détail :)
La documentation est trompeuse et incomplète. Les méthodes objectID ne copient pas elles-mêmes les objets, elles garantissent simplement que vous avez obtenu l'objet spécifique que vous vouliez.
Le context2
dans l'exemple est en fait le contexte source et non la destination. Vous obtenez un nil car le contexte de destination n'a pas d'objet avec cet ID.
La copie d'objets gérés est assez complexe en raison de la complexité du graphique d'objet et de la façon dont le contexte gère le graphique. Vous devez recréer l'objet copié en détail dans le nouveau contexte.
Voici un exemple de code que j'ai extrait de l'exemple de code pour The Pragmatic Programmer's Core Data: l'API d'Apple pour la persistance des données sur Mac OS X . (Vous pourrez peut-être télécharger l'intégralité du code du projet sans acheter le livre sur le site Pragmatic.) Il devrait vous donner une idée approximative de la façon de copier un objet entre les contextes.
Vous pouvez créer un code de base qui copie des objets, mais les détails des relations de chaque graphique d'objet signifient généralement que vous devez personnaliser pour chaque modèle de données.
J'ai eu le même problème moi-même et j'ai trouvé cet article sur les entités déconnectées créées qui pourraient plus tard être ajoutées au contexte: http://locassa.com/temporary-storage-in-apples-coredata/
L'idée est que vous avez un NSManagedObject parce que vous allez stocker des objets dans la base de données. Mon obstacle était que beaucoup de ces objets sont téléchargés via l'API HTTP et je veux en jeter la plupart à la fin de la session. Pensez à un flux de messages d'utilisateurs, et je veux uniquement enregistrer ceux qui ont été préférés ou enregistrés en tant que brouillon.
Je crée tous mes messages en utilisant
+ (id)newPost {
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Post" inManagedObjectContext:self.managedObjectContext];
Post *post = [[Post alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:nil];
return post;
}
puis les publications sont insérées dans le contexte de l'objet géré local lorsqu'elles sont favorisées
+ (BOOL)favoritePost:(Post *)post isFavorite:(BOOL)isFavorite
{
// Set the post's isFavorite flag
post.isFavorite = [NSNumber numberWithBool:isFavorite];
// If the post is being favorited and not yet in the local database, add it
NSError *error;
if (isFavorite && [self.managedObjectContext existingObjectWithID:post.objectID error:&error] == nil) {
[self.managedObjectContext insertObject:post];
}
// Else if the post is being un-favorited and is in the local database, delete it
else if (!isFavorite && [self.managedObjectContext existingObjectWithID:post.objectID error:&error] != nil) {
[self.managedObjectContext deleteObject:post];
}
// If there was an error, output and return NO to indicate a failure
if (error) {
NSLog(@"error: %@", error);
return NO;
}
return YES;
}
J'espère que cela pourra aider.
Vous devez vous assurer que vous enregistrez le contexte dans lequel managedObject
réside. Pour récupérer le même objet dans un contexte différent, il doit être présent dans le magasin persistant.
Selon la documentation , objectWithID:
renvoie toujours un objet. Ainsi, le fait que l'erreur se résout en un objet aura toutes les valeurs nil
implique qu'il ne trouve pas votre objet dans le magasin persistant.
Swift 5
Si vous vous demandez comment copier NSManagedObjects en 2020, le code ci-dessous fonctionne pour moi:
// `Restaurant` is the name of my managed object subclass.
// I like to have Xcode auto generate my subclasses (Codegen
// set to "Class Definition") & then just extend them with
// whatever functionality I need.
extension Restaurant {
public func copy() -> Restaurant? {
let attributes = entity.attributesByName.map { $0.key }
let dictionary = dictionaryWithValues(forKeys: attributes)
guard let context = AppDelegate.shared?.persistentContainer.viewContext,
let restaurantCopy = NSEntityDescription.insertNewObject(forEntityName: Restaurant.entityName, into: context) as? Restaurant
else
{
return nil
}
restaurantCopy.setValuesForKeys(dictionary)
return restaurantCopy
}
}