Эти ответы очень сблизили меня, хотя, похоже, они имели некоторые недостатки:
Во-первых, я воспользовался советом Z S и сделал его категорией для NSManagedObject, мне это показалось чище.
2-й, Мой граф объектов содержит отношения «один к одному», поэтому я начал с примера levous, но учтите, что пример levous не клонирует объект в случае отношения to-one. Это вызовет сбой (попытка сохранить NSMO из одного контекста в другом контексте). Я обратился к этому в приведенном ниже примере.
В-третьих, я предоставил кэш уже клонированных объектов, это предотвращает клонирование объектов дважды и, следовательно, дублирует их в новом графе объектов, а также предотвращает циклы.
4-й, я добавил черный список (список типов сущностей, которые нельзя клонировать). Я сделал это частично, чтобы устранить один недостаток моего окончательного решения, которое я опишу ниже.
ПРИМЕЧАНИЕ: если вы используете то, что я понимаю, в соответствии с рекомендациями CoreData, всегда обеспечивающими обратные отношения, то это, вероятно, клонирует все объекты, имеющие отношение к объекту, который вы хотите клонировать. Если вы используете инверсии и у вас есть один корневой объект, который знает обо всех других объектах, то вы, вероятно, будете клонировать все это. Моим решением было добавить черный список и передать тип сущности, который, как я знал, был родителем одного из объектов, которые я хотел клонировать. Похоже, это работает для меня. :)
Счастливого клонирования!
// 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