如何复制或复制核心数据pipe理对象?

我有一个托pipe对象(“A”),其中包含各种属性和types的关系,其关系也有自己的属性和关系。 我想要做的是“复制”或“复制”以对象“A”为根的整个对象graphics,从而创build一个非常类似于“A”的新对象“B”。

更具体地说,“B”(或其子女)所包含的关系中没有一个指向与“A”有关的对象。 应该有一个完全相同的关系完整的新对象图,并具有相同的属性,但当然不同的ID的所有对象。

有一个明显的手动方法来做到这一点,但我希望能够从Core Data文档中学到更简单的方法。

TIA!

这是我创build的一个类,用于执行托pipe对象的“深层复制”:属性和关系。 请注意,这不会检查对象图中的循环。 (感谢Jaanus的出发点…)

@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 

这些答案让我非常接近,虽然他们似乎有一些缺点:

第一,我接受了ZS的build议,并将其作为NSManagedObject的一个类别,这对我来说似乎更清洁一些。

第二,我的对象图包含一对一的关系,所以我从这个例子开始,但是请注意,在一对一关系的情况下,这个例子并不是克隆对象。 这将导致崩溃(尝试从另一个上下文中的一个上下文保存NSMO)。 我在下面的例子中解决了这个问题。

第三,我提供了一个已经被克隆的对象的caching,这可以防止对象被克隆两次,因此在新的对象图中被复制,也防止了循环。

第四,我已经添加了一个黑名单(实体types的列表不克隆)。 我这样做是为了解决我最终解决scheme的一个缺点,我将在下面介绍。

注意:如果您使用我所了解的CoreData最佳实践,始终提供反向关系,那么这可能会克隆与要克隆的对象具有关系的所有对象。 如果你正在使用逆,并且你有一个知道所有其他对象的单根对象,那么你可能会克隆整个事情。 我的解决scheme是添加黑名单,并通过实体types,我知道是我想克隆的对象之一的父项。 这似乎对我有用。 🙂

快乐的克隆!

 // 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 

我更新了user353759的答案来支持一个关系。

 @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; } 

这是@Derricks的答案,通过询问关系来查看它是否被sorting,通过修改来支持新的iOS 6.0有序对多关系。 当我在那里的时候,我在同一个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 

我注意到目前的答案有几个错误。 首先,当迭代它时,似乎有一些东西似乎正在改变许多相关对象的集合。 其次,我不确定在API中是否发生了变化,但是使用NSRelationshipDescription的string表示作为关键字时,抓取这些相关对象时会抛出exception。

我做了一些调整,做了一些基本的testing,似乎工作。 如果有人想进一步调查,这将是伟大的!

 @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 

我修改了Derrick的答案 ,这对我来说非常合适,可以支持iOS 5.0和Mac OS X 10.7中的有序关系 :

 // // NSManagedObject+Clone.h // #import <CoreData/CoreData.h> #ifndef CD_CUSTOM_DEBUG_LOG #define CD_CUSTOM_DEBUG_LOG NSLog #endif @interface NSManagedObject (Clone) - (NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context excludeEntities:(NSArray *)namesOfEntitiesToExclude; @end // // NSManagedObject+Clone.m // #import "NSManagedObject+Clone.h" @interface NSManagedObject (ClonePrivate) - (NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context withCopiedCache:(NSMutableDictionary **)alreadyCopied excludeEntities:(NSArray *)namesOfEntitiesToExclude; @end @implementation NSManagedObject (Clone) - (NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context withCopiedCache:(NSMutableDictionary **)alreadyCopied excludeEntities:(NSArray *)namesOfEntitiesToExclude { if (!context) { CD_CUSTOM_DEBUG_LOG(@"%@:%@ Try to clone NSManagedObject in the 'nil' context.", THIS_CLASS, THIS_METHOD); return nil; } NSString *entityName = [[self entity] name]; if ([namesOfEntitiesToExclude containsObject:entityName]) { return nil; } NSManagedObject *cloned = nil; if (alreadyCopied != NULL) { cloned = [*alreadyCopied objectForKey:[self objectID]]; if (cloned) { return cloned; } // Create new object in data store cloned = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context]; [*alreadyCopied setObject:cloned forKey:[self objectID]]; } else { CD_CUSTOM_DEBUG_LOG(@"%@:%@ NULL pointer was passed in 'alreadyCopied' argument.", THIS_CLASS, THIS_METHOD); } // 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]; NSArray *relationshipKeys = [relationships allKeys]; for (NSString *relName in relationshipKeys) { NSRelationshipDescription *rel = [relationships objectForKey:relName]; NSString *keyName = [rel name]; if ([rel isToMany]) { if ([rel isOrdered]) { // Get a set of all objects in the relationship NSMutableOrderedSet *sourceSet = [self mutableOrderedSetValueForKey:keyName]; NSMutableOrderedSet *clonedSet = [cloned mutableOrderedSetValueForKey:keyName]; for (id relatedObject in sourceSet) { //Clone it, and add clone to set NSManagedObject *clonedRelatedObject = [relatedObject cloneInContext:context withCopiedCache:alreadyCopied excludeEntities:namesOfEntitiesToExclude]; if (clonedRelatedObject) { [clonedSet addObject:clonedRelatedObject]; } } } else { // Get a set of all objects in the relationship NSMutableSet *sourceSet = [self mutableSetValueForKey:keyName]; NSMutableSet *clonedSet = [cloned mutableSetValueForKey:keyName]; for (id relatedObject in sourceSet) { //Clone it, and add clone to set NSManagedObject *clonedRelatedObject = [relatedObject cloneInContext:context withCopiedCache:alreadyCopied excludeEntities:namesOfEntitiesToExclude]; if (clonedRelatedObject) { [clonedSet addObject:clonedRelatedObject]; } } } } else { NSManagedObject *relatedObject = [self valueForKey:keyName]; if (relatedObject) { NSManagedObject *clonedRelatedObject = [relatedObject cloneInContext:context withCopiedCache:alreadyCopied excludeEntities:namesOfEntitiesToExclude]; [cloned setValue:clonedRelatedObject forKey:keyName]; } } } return cloned; } - (NSManagedObject *)cloneInContext:(NSManagedObjectContext *)context excludeEntities:(NSArray *)namesOfEntitiesToExclude { NSMutableDictionary* mutableDictionary = [NSMutableDictionary dictionary]; return [self cloneInContext:context withCopiedCache:&mutableDictionary excludeEntities:namesOfEntitiesToExclude]; } @end 

我真的需要解决@derrick在其原始答案中承认的大规模复制问题。 我修改了MasonK的版本。 这并没有很多以前版本的优雅, 但它似乎解决了我的应用程序中的一个关键问题(类似实体的意外重复)。

 // // 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 

像这样的东西? (未经testing)这将是您提到的“手动方式”,但会自动与模型更改同步,因此您不必手动input所有属性名称。

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 } } 

Objective-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 

这将返回一个具有所有属性的克隆,并且不会复制任何关系。

你所要求的就是所谓的“深层复制”。 因为它可能非常昂贵(如无限内存使用情况),而且很难正确使用(考虑对象图中的循环),因此Core Data不提供此function。

通常有一种架构可以避免这种需求。 也许你可以创build一个新的实体来封装差异(或未来的差异),而不是复制整个对象图,如果你复制对象图,然后引用原始图。 换句话说,实例化一个新的“定制者”实体,不要复制整个对象图。 例如,考虑一组排屋。 每个都有相同的框架和电器,但业主可以定制油漆和家具。 而不是为每个所有者深层复制整个房屋graphics,而是为每个所有者build立一个“绘画和家具”实体 – 即所有者和房屋模型。

与维护关系的最佳DEEP COPY类别是https://gist.github.com/advantis/7642084

这被称为“深层复制”。 因为它可能会令人惊讶的昂贵,很多语言/库不支持开箱即用,并要求您自己推出。 cocoa不幸是其中之一。

在这里,你有我的快速3方法:

 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 } 

也:

 [clone setValuesForKeysWithDictionary:[item dictionaryWithValuesForKeys:[properties allKeys]]]; [clone setValuesForKeysWithDictionary:[item dictionaryWithValuesForKeys:[attributes allKeys]]]; 

如果你只想关联Hierarchie关系中的实体,你只需要添加下面的代码到Dmitry的解决scheme

在这之间

 NSString *entityName = [[self entity] name]; 

这里if([namesOfEntitiesToExclude containsObject:entityName]){

 NSMutableArray *arrayToOnlyRelate = [NSMutableArray arrayWithObjects:@"ENTITY 1",@"ENTITY 2",@"ENTITY 3", nil]; if ([arrayToOnlyRelate containsObject:entityName]) { return self; } 

My take on this is at https://gist.github.com/jpmhouston/7958fceae9216f69178d4719a3492577

  • passes rel.inverseRelationship.name into the recursive method to omit visiting the inverse relationships rather than maintaining a set of alreadyCopied objects

  • shallow or deep copies

  • accepts keypaths of relationships to not clone, but to either omit, or to simply copy if the inverse is a to-many relationship

  • workaround for ordered,to-many relationships ending up in backwards order – simply iterate over the source entities backwards 🙂 i'm not sure if this a good idea or if it even works all the time

Feedback & comments welcome, especially if someone can elaborate on Benjohn 's comment on mis-ordering above " The work around for this is to build the complete ordered set and then assign using the primitive KVO variant. " and can improve my ordered,to-many workaround.

Also, I'm using MagicalRecord and so my code assumes that, including providing easy methods that use its default context.