J'ai un objet géré ("A") qui contient divers attributs et types de relations, et ses relations ont également leurs propres attributs et relations. Ce que je voudrais faire, c'est "copier" ou "dupliquer" le graphe d'objet entier enraciné à l'objet "A", et ainsi créer un nouvel objet "B" qui est très similaire à "A".
Pour être plus précis, aucune des relations contenues par "B" (ou ses enfants) ne doit pointer vers des objets liés à "A". Il devrait y avoir un graphique d'objet entièrement nouveau avec des relations similaires intactes, et tous les objets ayant les mêmes attributs, mais bien sûr des identifiants différents.
Il existe une manière manuelle évidente de le faire, mais j'espérais découvrir un moyen plus simple de le faire qui n'était pas totalement apparent dans la documentation de Core Data.
TIA!
Voici une classe que j'ai créée pour effectuer une "copie complète" des objets gérés: attributs et relations. Notez que cela ne vérifie pas les boucles dans le graphique d'objet. (Merci Jaanus pour le point de départ ...)
@interface ManagedObjectCloner : NSObject {
}
+(NSManagedObject *)clone:(NSManagedObject *)source inContext:(NSManagedObjectContext *)context;
@end
@implementation ManagedObjectCloner
+(NSManagedObject *) clone:(NSManagedObject *)source inContext:(NSManagedObjectContext *)context{
NSString *entityName = [[source entity] name];
//create new object in data store
NSManagedObject *cloned = [NSEntityDescription
insertNewObjectForEntityForName:entityName
inManagedObjectContext:context];
//loop through all attributes and assign then to the clone
NSDictionary *attributes = [[NSEntityDescription
entityForName:entityName
inManagedObjectContext:context] attributesByName];
for (NSString *attr in attributes) {
[cloned setValue:[source valueForKey:attr] forKey:attr];
}
//Loop through all relationships, and clone them.
NSDictionary *relationships = [[NSEntityDescription
entityForName:entityName
inManagedObjectContext:context] relationshipsByName];
for (NSRelationshipDescription *rel in relationships){
NSString *keyName = [NSString stringWithFormat:@"%@",rel];
//get a set of all objects in the relationship
NSMutableSet *sourceSet = [source mutableSetValueForKey:keyName];
NSMutableSet *clonedSet = [cloned mutableSetValueForKey:keyName];
NSEnumerator *e = [sourceSet objectEnumerator];
NSManagedObject *relatedObject;
while ( relatedObject = [e nextObject]){
//Clone it, and add clone to set
NSManagedObject *clonedRelatedObject = [ManagedObjectCloner clone:relatedObject
inContext:context];
[clonedSet addObject:clonedRelatedObject];
}
}
return cloned;
}
@end
Ces réponses m'ont vraiment rapproché, même si elles semblaient présenter des lacunes:
Tout d'abord, j'ai suivi les conseils de Z S et en ai fait une catégorie sur NSManagedObject, cela m'a paru un peu plus propre.
Deuxièmement, mon graphique d'objet contient des relations de un à un, alors je suis parti de l'exemple de levous, mais notez que l'exemple de levous ne clone pas l'objet dans le cas de la relation de un. Cela entraînera un plantage (tentative de sauvegarde d'un NSMO d'un contexte dans un contexte différent). J'ai abordé cela dans l'exemple ci-dessous.
3ème, j'ai fourni un cache d'objets déjà clonés, cela empêche les objets d'être clonés deux fois et donc dupliqués dans le nouveau graphique d'objet, et empêche également les cycles.
4ème, j'ai ajouté une liste noire (liste des types d'entités à ne pas cloner). J'ai fait cela en partie pour résoudre une lacune de ma solution finale, que je décrirai ci-dessous.
REMARQUE: si vous utilisez ce que je comprends comme une bonne pratique CoreData, fournissant toujours des relations inverses, cela clonera probablement tous les objets qui ont une relation avec l'objet que vous souhaitez cloner. Si vous utilisez des inverses et que vous avez un seul objet racine qui connaît tous les autres objets, vous clonerez probablement le tout. Ma solution à cela a été d'ajouter la liste noire et de passer le type d'entité que je savais être le parent d'un des objets que je voulais cloner. Cela semble fonctionner pour moi. :)
Bon clonage!
// NSManagedObject+Clone.h
#import <CoreData/CoreData.h>
@interface NSManagedObject (Clone)
- (NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context exludeEntities:(NSArray *)namesOfEntitiesToExclude;
@end
// NSManagedObject+Clone.m
#import "NSManagedObject+Clone.h"
@implementation NSManagedObject (Clone)
- (NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context withCopiedCache:(NSMutableDictionary *)alreadyCopied exludeEntities:(NSArray *)namesOfEntitiesToExclude {
NSString *entityName = [[self entity] name];
if ([namesOfEntitiesToExclude containsObject:entityName]) {
return nil;
}
NSManagedObject *cloned = [alreadyCopied objectForKey:[self objectID]];
if (cloned != nil) {
return cloned;
}
//create new object in data store
cloned = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];
[alreadyCopied setObject:cloned forKey:[self objectID]];
//loop through all attributes and assign then to the clone
NSDictionary *attributes = [[NSEntityDescription entityForName:entityName inManagedObjectContext:context] attributesByName];
for (NSString *attr in attributes) {
[cloned setValue:[self valueForKey:attr] forKey:attr];
}
//Loop through all relationships, and clone them.
NSDictionary *relationships = [[NSEntityDescription entityForName:entityName inManagedObjectContext:context] relationshipsByName];
for (NSString *relName in [relationships allKeys]){
NSRelationshipDescription *rel = [relationships objectForKey:relName];
NSString *keyName = rel.name;
if ([rel isToMany]) {
//get a set of all objects in the relationship
NSMutableSet *sourceSet = [self mutableSetValueForKey:keyName];
NSMutableSet *clonedSet = [cloned mutableSetValueForKey:keyName];
NSEnumerator *e = [sourceSet objectEnumerator];
NSManagedObject *relatedObject;
while ( relatedObject = [e nextObject]){
//Clone it, and add clone to set
NSManagedObject *clonedRelatedObject = [relatedObject cloneInContext:context withCopiedCache:alreadyCopied exludeEntities:namesOfEntitiesToExclude];
[clonedSet addObject:clonedRelatedObject];
}
}else {
NSManagedObject *relatedObject = [self valueForKey:keyName];
if (relatedObject != nil) {
NSManagedObject *clonedRelatedObject = [relatedObject cloneInContext:context withCopiedCache:alreadyCopied exludeEntities:namesOfEntitiesToExclude];
[cloned setValue:clonedRelatedObject forKey:keyName];
}
}
}
return cloned;
}
- (NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context exludeEntities:(NSArray *)namesOfEntitiesToExclude {
return [self cloneInContext:context withCopiedCache:[NSMutableDictionary dictionary] exludeEntities:namesOfEntitiesToExclude];
}
@end
J'ai mis à jour la réponse de user353759 pour prendre en charge les relations toOne.
@interface ManagedObjectCloner : NSObject {
}
+(NSManagedObject *)clone:(NSManagedObject *)source inContext:(NSManagedObjectContext *)context;
@end
@implementation ManagedObjectCloner
+(NSManagedObject *) clone:(NSManagedObject *)source inContext:(NSManagedObjectContext *)context{
NSString *entityName = [[source entity] name];
//create new object in data store
NSManagedObject *cloned = [NSEntityDescription
insertNewObjectForEntityForName:entityName
inManagedObjectContext:context];
//loop through all attributes and assign then to the clone
NSDictionary *attributes = [[NSEntityDescription
entityForName:entityName
inManagedObjectContext:context] attributesByName];
for (NSString *attr in attributes) {
[cloned setValue:[source valueForKey:attr] forKey:attr];
}
//Loop through all relationships, and clone them.
NSDictionary *relationships = [[NSEntityDescription
entityForName:entityName
inManagedObjectContext:context] relationshipsByName];
for (NSString *relName in [relationships allKeys]){
NSRelationshipDescription *rel = [relationships objectForKey:relName];
NSString *keyName = [NSString stringWithFormat:@"%@",rel];
if ([rel isToMany]) {
//get a set of all objects in the relationship
NSMutableSet *sourceSet = [source mutableSetValueForKey:keyName];
NSMutableSet *clonedSet = [cloned mutableSetValueForKey:keyName];
NSEnumerator *e = [sourceSet objectEnumerator];
NSManagedObject *relatedObject;
while ( relatedObject = [e nextObject]){
//Clone it, and add clone to set
NSManagedObject *clonedRelatedObject = [ManagedObjectCloner clone:relatedObject
inContext:context];
[clonedSet addObject:clonedRelatedObject];
}
}else {
[cloned setValue:[source valueForKey:keyName] forKey:keyName];
}
}
return cloned;
}
Il s'agit de la réponse @Derricks, modifiée pour prendre en charge les nouvelles relations ordonnées à plusieurs d'iOS 6.0 en interrogeant la relation pour voir si elle est ordonnée. Pendant que j'étais là-bas, j'ai ajouté une méthode de clonage plus simple pour le cas commun de clonage au sein du même NSManagedObjectContext.
//
// NSManagedObject+Clone.h
// Tone Poet
//
// Created by Mason Kramer on 5/31/13.
// Copyright (c) 2013 Mason Kramer. The contents of this file are available for use by anyone, for any purpose whatsoever.
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@interface NSManagedObject (Clone) {
}
-(NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context withCopiedCache:(NSMutableDictionary *)alreadyCopied exludeEntities:(NSArray *)namesOfEntitiesToExclude;
-(NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context exludeEntities:(NSArray *)namesOfEntitiesToExclude;
-(NSManagedObject *) clone;
@end
//
// NSManagedObject+Clone.m
// Tone Poet
//
// Created by Mason Kramer on 5/31/13.
// Copyright (c) 2013 Mason Kramer. The contents of this file are available for use by anyone, for any purpose whatsoever.
//
#import "NSManagedObject+Clone.h"
@implementation NSManagedObject (Clone)
-(NSManagedObject *) clone {
return [self cloneInContext:[self managedObjectContext] exludeEntities:@[]];
}
- (NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context withCopiedCache:(NSMutableDictionary *)alreadyCopied exludeEntities:(NSArray *)namesOfEntitiesToExclude {
NSString *entityName = [[self entity] name];
if ([namesOfEntitiesToExclude containsObject:entityName]) {
return nil;
}
NSManagedObject *cloned = [alreadyCopied objectForKey:[self objectID]];
if (cloned != nil) {
return cloned;
}
//create new object in data store
cloned = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];
[alreadyCopied setObject:cloned forKey:[self objectID]];
//loop through all attributes and assign then to the clone
NSDictionary *attributes = [[NSEntityDescription entityForName:entityName inManagedObjectContext:context] attributesByName];
for (NSString *attr in attributes) {
[cloned setValue:[self valueForKey:attr] forKey:attr];
}
//Loop through all relationships, and clone them.
NSDictionary *relationships = [[NSEntityDescription entityForName:entityName inManagedObjectContext:context] relationshipsByName];
for (NSString *relName in [relationships allKeys]){
NSRelationshipDescription *rel = [relationships objectForKey:relName];
NSString *keyName = rel.name;
if ([rel isToMany]) {
if ([rel isOrdered]) {
NSMutableOrderedSet *sourceSet = [self mutableOrderedSetValueForKey:keyName];
NSMutableOrderedSet *clonedSet = [cloned mutableOrderedSetValueForKey:keyName];
NSEnumerator *e = [sourceSet objectEnumerator];
NSManagedObject *relatedObject;
while ( relatedObject = [e nextObject]){
//Clone it, and add clone to set
NSManagedObject *clonedRelatedObject = [relatedObject cloneInContext:context withCopiedCache:alreadyCopied exludeEntities:namesOfEntitiesToExclude];
[clonedSet addObject:clonedRelatedObject];
[clonedSet addObject:clonedRelatedObject];
}
}
else {
NSMutableSet *sourceSet = [self mutableSetValueForKey:keyName];
NSMutableSet *clonedSet = [cloned mutableSetValueForKey:keyName];
NSEnumerator *e = [sourceSet objectEnumerator];
NSManagedObject *relatedObject;
while ( relatedObject = [e nextObject]){
//Clone it, and add clone to set
NSManagedObject *clonedRelatedObject = [relatedObject cloneInContext:context withCopiedCache:alreadyCopied exludeEntities:namesOfEntitiesToExclude];
[clonedSet addObject:clonedRelatedObject];
}
}
}
else {
NSManagedObject *relatedObject = [self valueForKey:keyName];
if (relatedObject != nil) {
NSManagedObject *clonedRelatedObject = [relatedObject cloneInContext:context withCopiedCache:alreadyCopied exludeEntities:namesOfEntitiesToExclude];
[cloned setValue:clonedRelatedObject forKey:keyName];
}
}
}
return cloned;
}
-(NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context exludeEntities:(NSArray *)namesOfEntitiesToExclude {
return [self cloneInContext:context withCopiedCache:[NSMutableDictionary dictionary] exludeEntities:namesOfEntitiesToExclude];
}
@end
J'ai remarqué quelques bugs avec les réponses actuelles. Premièrement, quelque chose semble muter l'ensemble des objets liés à plusieurs comme étant itéré. Deuxièmement, je ne sais pas si quelque chose a changé dans l'API, mais l'utilisation de la représentation String de NSRelationshipDescription
comme clé générait des exceptions lors de la capture de ces objets associés.
J'ai fait quelques ajustements, j'ai fait quelques tests de base et cela semble fonctionner. Si quelqu'un veut enquêter plus avant, ce serait formidable!
@implementation NSManagedObjectContext (DeepCopy)
-(NSManagedObject *) clone:(NSManagedObject *)source{
NSString *entityName = [[source entity] name];
//create new object in data store
NSManagedObject *cloned = [NSEntityDescription
insertNewObjectForEntityForName:entityName
inManagedObjectContext:self];
//loop through all attributes and assign then to the clone
NSDictionary *attributes = [[NSEntityDescription
entityForName:entityName
inManagedObjectContext:self] attributesByName];
for (NSString *attr in attributes) {
[cloned setValue:[source valueForKey:attr] forKey:attr];
}
//Loop through all relationships, and clone them.
NSDictionary *relationships = [[NSEntityDescription
entityForName:entityName
inManagedObjectContext:self] relationshipsByName];
for (NSString *relName in [relationships allKeys]){
NSRelationshipDescription *rel = [relationships objectForKey:relName];
if ([rel isToMany]) {
//get a set of all objects in the relationship
NSArray *sourceArray = [[source mutableSetValueForKey:relName] allObjects];
NSMutableSet *clonedSet = [cloned mutableSetValueForKey:relName];
for(NSManagedObject *relatedObject in sourceArray) {
NSManagedObject *clonedRelatedObject = [self clone:relatedObject];
[clonedSet addObject:clonedRelatedObject];
}
} else {
[cloned setValue:[source valueForKey:relName] forKey:relName];
}
}
return cloned;
}
@end
J'avais un réel besoin de contourner le problème de la copie de masse que @derrick a reconnu dans sa réponse originale. J'ai modifié la version de MasonK. Cela n'a pas beaucoup d'élégance qui était dans les versions précédentes; mais il semble résoudre un problème clé (doublons involontaires d'entités similaires) dans mon application.
//
// NSManagedObject+Clone.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@interface NSManagedObject (Clone) {
}
-(NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context withCopiedCache:(NSMutableDictionary *)alreadyCopied exludeEntities:(NSMutableArray *)namesOfEntitiesToExclude isFirstPass:(BOOL)firstPass;
-(NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context exludeEntities:(NSMutableArray *)namesOfEntitiesToExclude;
-(NSManagedObject *) clone;
@end
//
// NSManagedObject+Clone.m
//
#import "NSManagedObject+Clone.h"
@implementation NSManagedObject (Clone)
-(NSManagedObject *) clone {
NSMutableArray *emptyArray = [NSMutableArray arrayWithCapacity:1];
return [self cloneInContext:[self managedObjectContext] exludeEntities:emptyArray];
}
- (NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context withCopiedCache:(NSMutableDictionary *)alreadyCopied exludeEntities:(NSMutableArray *)namesOfEntitiesToExclude
isFirstPass:(BOOL)firstPass
{
NSString *entityName = [[self entity] name];
if ([namesOfEntitiesToExclude containsObject:entityName]) {
return nil;
}
NSManagedObject *cloned = [alreadyCopied objectForKey:[self objectID]];
if (cloned != nil) {
return cloned;
}
//create new object in data store
cloned = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];
[alreadyCopied setObject:cloned forKey:[self objectID]];
//loop through all attributes and assign then to the clone
NSDictionary *attributes = [[NSEntityDescription entityForName:entityName inManagedObjectContext:context] attributesByName];
for (NSString *attr in attributes) {
[cloned setValue:[self valueForKey:attr] forKey:attr];
}
//Inverse relationships can cause all of the entities under one area to get duplicated
//This is the reason for "isFirstPass" and "excludeEntities"
if (firstPass == TRUE) {
[namesOfEntitiesToExclude addObject:entityName];
firstPass=FALSE;
}
//Loop through all relationships, and clone them.
NSDictionary *relationships = [[NSEntityDescription entityForName:entityName inManagedObjectContext:context] relationshipsByName];
for (NSString *relName in [relationships allKeys]){
NSRelationshipDescription *rel = [relationships objectForKey:relName];
NSString *keyName = rel.name;
if ([rel isToMany]) {
if ([rel isOrdered]) {
NSMutableOrderedSet *sourceSet = [self mutableOrderedSetValueForKey:keyName];
NSMutableOrderedSet *clonedSet = [cloned mutableOrderedSetValueForKey:keyName];
NSEnumerator *e = [sourceSet objectEnumerator];
NSManagedObject *relatedObject;
while ( relatedObject = [e nextObject]){
//Clone it, and add clone to set
NSManagedObject *clonedRelatedObject = [relatedObject cloneInContext:context withCopiedCache:alreadyCopied exludeEntities:namesOfEntitiesToExclude
isFirstPass:firstPass];
if (clonedRelatedObject != nil) {
[clonedSet addObject:clonedRelatedObject];
[clonedSet addObject:clonedRelatedObject];
}
}
}
else {
NSMutableSet *sourceSet = [self mutableSetValueForKey:keyName];
NSMutableSet *clonedSet = [cloned mutableSetValueForKey:keyName];
NSEnumerator *e = [sourceSet objectEnumerator];
NSManagedObject *relatedObject;
while ( relatedObject = [e nextObject]){
//Clone it, and add clone to set
NSManagedObject *clonedRelatedObject = [relatedObject cloneInContext:context withCopiedCache:alreadyCopied exludeEntities:namesOfEntitiesToExclude
isFirstPass:firstPass];
if (clonedRelatedObject != nil) {
[clonedSet addObject:clonedRelatedObject];
}
}
}
}
else {
NSManagedObject *relatedObject = [self valueForKey:keyName];
if (relatedObject != nil) {
NSManagedObject *clonedRelatedObject = [relatedObject cloneInContext:context withCopiedCache:alreadyCopied exludeEntities:namesOfEntitiesToExclude
isFirstPass:firstPass];
if (clonedRelatedObject != nil) {
[cloned setValue:clonedRelatedObject forKey:keyName];
}
}
}
}
return cloned;
}
-(NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context exludeEntities:(NSMutableArray *)namesOfEntitiesToExclude {
return [self cloneInContext:context withCopiedCache:[NSMutableDictionary dictionary] exludeEntities:namesOfEntitiesToExclude isFirstPass:TRUE];
}
@end
Quelque chose comme ça? (non testé) Ce serait la "manière manuelle" que vous mentionnez, mais elle serait automatiquement synchronisée avec les changements de modèle et ainsi vous n'auriez pas à entrer manuellement tous les noms d'attributs.
Swift 3:
extension NSManagedObject {
func shallowCopy() -> NSManagedObject? {
guard let context = managedObjectContext, let entityName = entity.name else { return nil }
let copy = NSEntityDescription.insertNewObject(forEntityName: entityName, into: context)
let attributes = entity.attributesByName
for (attrKey, _) in attributes {
copy.setValue(value(forKey: attrKey), forKey: attrKey)
}
return copy
}
}
Objectif-C:
@interface MyObject (Clone)
- (MyObject *)clone;
@end
@implementation MyObject (Clone)
- (MyObject *)clone{
MyObject *cloned = [NSEntityDescription
insertNewObjectForEntityForName:@"MyObject"
inManagedObjectContext:moc];
NSDictionary *attributes = [[NSEntityDescription
entityForName:@"MyObject"
inManagedObjectContext:moc] attributesByName];
for (NSString *attr in attributes) {
[cloned setValue:[self valueForKey:attr] forKey:attr];
}
return cloned;
}
@end
Cela vous renverra un clone avec tous les attributs et aucune relation copiée.
Ce que vous demandez s'appelle une "copie complète". Parce que cela peut être très coûteux (comme dans l'utilisation de la mémoire illimitée) et très difficile à obtenir correctement (pensez aux boucles dans le graphique d'objet), Core Data ne fournit pas cette fonctionnalité pour vous.
Il existe souvent une architecture qui évite cependant le besoin. Au lieu de faire une copie d'un graphique d'objet entier, vous pouvez peut-être créer une nouvelle entité qui encapsule les différences (ou les différences futures) que vous auriez si vous copiez le graphique d'objet et référence ensuite uniquement le graphique d'origine. En d'autres termes, instanciez une nouvelle entité de "personnalisation" et ne copiez pas le graphique d'objet entier. Par exemple, considérons un ensemble de maisons en rangée. Chacun a un cadre et des appareils identiques, mais le propriétaire peut personnaliser la peinture et les meubles. Au lieu de copier en profondeur le graphique de la maison entière pour chaque propriétaire, ayez une entité "peinture et mobilier" - qui fait référence au propriétaire et au modèle de maison - pour chaque propriétaire.
Voici mon approche Swift:
func shallowCopy(copyRelations: Bool) -> NSManagedObject? {
guard let context = managedObjectContext, let entityName = entity.name else { return nil }
let copy = NSEntityDescription.insertNewObject(forEntityName: entityName, into: context)
let attributes = entity.attributesByName
for (attrKey, _) in attributes {
copy.setValue(value(forKey: attrKey), forKey: attrKey)
}
if copyRelations {
let relations = entity.relationshipsByName
for (relKey, relValue) in relations {
if relValue.isToMany {
let sourceSet = mutableSetValue(forKey: relKey)
let clonedSet = copy.mutableSetValue(forKey: relKey)
let enumerator = sourceSet.objectEnumerator()
while let relatedObject = enumerator.nextObject() {
let clonedRelatedObject = (relatedObject as! NSManagedObject).shallowCopy(copyRelations: false)
clonedSet.add(clonedRelatedObject!)
}
} else {
copy.setValue(value(forKey: relKey), forKey: relKey)
}
}
}
return copy
}
C'est ce qu'on appelle une "copie complète". Parce qu'il peut être étonnamment cher, de nombreuses langues/bibliothèques ne le prennent pas en charge et vous obligent à rouler le vôtre. Le cacao en fait malheureusement partie.
Si vous ne souhaitez associer que des entités dans la hiérarchie des relations, il vous suffit d'ajouter le code suivant à la solution de Dmitry
Entre ce
NSString *entityName = [[self entity] name];
ICI si ([namesOfEntitiesToExclude contientObject: entityName]) {
NSMutableArray *arrayToOnlyRelate = [NSMutableArray arrayWithObjects:@"ENTITY 1",@"ENTITY 2",@"ENTITY 3", nil];
if ([arrayToOnlyRelate containsObject:entityName]) {
return self;
}
Aussi:
[clone setValuesForKeysWithDictionary:[item dictionaryWithValuesForKeys:[properties allKeys]]];
[clone setValuesForKeysWithDictionary:[item dictionaryWithValuesForKeys:[attributes allKeys]]];
Mon avis à ce sujet est à https://Gist.github.com/jpmhouston/7958fceae9216f69178d4719a3492577
passe rel.inverseRelationship.name
dans la méthode récursive pour ne pas visiter les relations inverses plutôt que de conserver un ensemble d'objets alreadyCopied
copies superficielles ou profondes
accepte keypaths des relations avec pas clone, mais soit omettre, soit simplement copier si l'inverse est a to -beaucoup de relations
solution de contournement pour les relations ordonnées à plusieurs se terminant dans le sens inverse - itérez simplement sur les entités source vers l'arrière :) je ne sais pas si c'est une bonne idée ou si cela fonctionne même tout le temps
Les commentaires et commentaires sont les bienvenus, surtout si quelqu'un peut élaborer sur le commentaire de Benjohn sur les erreurs de commande ci-dessus " Le travail à faire pour cela est de construire la commande complète définir puis attribuer à l'aide de la variante KVO primitive. "et peut améliorer ma solution de contournement ordonnée à plusieurs.
En outre, j'utilise MagicalRecord et mon code suppose donc que, y compris en fournissant des méthodes simples qui utilisent son contexte par défaut.
Version Swift 4.0
import UIKit
import CoreData
class ManagedObjectCloner: NSObject {
static func cloneObject(source :NSManagedObject, context :NSManagedObjectContext) -> NSManagedObject{
let entityName = source.entity.name
let cloned = NSEntityDescription.insertNewObject(forEntityName: entityName!, into: context)
let attributes = NSEntityDescription.entity(forEntityName: entityName!, in: context)?.attributesByName
for (key,_) in attributes! {
cloned.setValue(source.value(forKey: key), forKey: key)
}
let relationships = NSEntityDescription.entity(forEntityName: entityName!, in: context)?.relationshipsByName
for (key,_) in relationships! {
let sourceSet = source.mutableSetValue(forKey: key)
let clonedSet = cloned.mutableSetValue(forKey: key)
let e = sourceSet.objectEnumerator()
var relatedObj = e.nextObject() as? NSManagedObject
while ((relatedObj) != nil) {
let clonedRelatedObject = ManagedObjectCloner.cloneObject(source: relatedObj!, context: context)
clonedSet.add(clonedRelatedObject)
relatedObj = e.nextObject() as? NSManagedObject
}
}
return cloned
}
}