我有一个托管对象(“A”),它包含各种属性和关系类型,它的关系也有自己的属性和关系。我想做的是“复制”或“复制”以“ A”为根的整个对象图,从而创建一个非常类似于“ A”的新对象“ B”。
更具体地说,“B”(或其子代)包含的所有关系都不应指向与“ A”相关的对象。应该有一个全新的对象图,其中包含相似的关系,所有对象具有相同的属性,但ID当然不同。
有一种显而易见的手动方法可以实现这一点,但我希望了解一种更简单的方法,这种方法并不完全明显在Core Data文档中。
TIA!
我有一个托管对象(“A”),它包含各种属性和关系类型,它的关系也有自己的属性和关系。我想做的是“复制”或“复制”以“ A”为根的整个对象图,从而创建一个非常类似于“ A”的新对象“ B”。
更具体地说,“B”(或其子代)包含的所有关系都不应指向与“ A”相关的对象。应该有一个全新的对象图,其中包含相似的关系,所有对象具有相同的属性,但ID当然不同。
有一种显而易见的手动方法可以实现这一点,但我希望了解一种更简单的方法,这种方法并不完全明显在Core Data文档中。
TIA!
这是我创建的一个类,用于执行托管对象的“深度复制”:属性和关系。请注意,这不会检查对象图中的循环。(感谢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
// 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的答案,经过修改以支持iOS 6.0新版本引入的有序to-many关系,通过查询关系来确定其是否有序。在那里,我添加了一个更简单的-clone方法,用于在同一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
[clonedSet addObject:clonedRelatedObject];
? - elsurudoSwift 5
这是在 @Derrick、@Dmitry Makarenko 和 @masonk 的贡献基础上构建的,将所有内容整合在一起,改进并将其转化为适合2020年的解决方案。
.
extension NSManagedObject {
func copyEntireObjectGraph(context: NSManagedObjectContext) -> NSManagedObject {
var cache = Dictionary<NSManagedObjectID, NSManagedObject>()
return cloneObject(context: context, cache: &cache)
}
func cloneObject(context: NSManagedObjectContext, cache alreadyCopied: inout Dictionary<NSManagedObjectID, NSManagedObject>) -> NSManagedObject {
guard let entityName = self.entity.name else {
fatalError("source.entity.name == nil")
}
if let storedCopy = alreadyCopied[self.objectID] {
return storedCopy
}
let cloned = NSEntityDescription.insertNewObject(forEntityName: entityName, into: context)
alreadyCopied[self.objectID] = cloned
if let attributes = NSEntityDescription.entity(forEntityName: entityName, in: context)?.attributesByName {
for key in attributes.keys {
cloned.setValue(self.value(forKey: key), forKey: key)
}
}
if let relationships = NSEntityDescription.entity(forEntityName: entityName, in: context)?.relationshipsByName {
for (key, value) in relationships {
if value.isToMany {
if let sourceSet = self.value(forKey: key) as? NSMutableOrderedSet {
guard let clonedSet = cloned.value(forKey: key) as? NSMutableOrderedSet else {
fatalError("Could not cast relationship \(key) to an NSMutableOrderedSet")
}
let enumerator = sourceSet.objectEnumerator()
var nextObject = enumerator.nextObject() as? NSManagedObject
while let relatedObject = nextObject {
let clonedRelatedObject = relatedObject.cloneObject(context: context, cache: &alreadyCopied)
clonedSet.add(clonedRelatedObject)
nextObject = enumerator.nextObject() as? NSManagedObject
}
} else if let sourceSet = self.value(forKey: key) as? NSMutableSet {
guard let clonedSet = cloned.value(forKey: key) as? NSMutableSet else {
fatalError("Could not cast relationship \(key) to an NSMutableSet")
}
let enumerator = sourceSet.objectEnumerator()
var nextObject = enumerator.nextObject() as? NSManagedObject
while let relatedObject = nextObject {
let clonedRelatedObject = relatedObject.cloneObject(context: context, cache: &alreadyCopied)
clonedSet.add(clonedRelatedObject)
nextObject = enumerator.nextObject() as? NSManagedObject
}
}
} else {
if let relatedObject = self.value(forKey: key) as? NSManagedObject {
let clonedRelatedObject = relatedObject.cloneObject(context: context, cache: &alreadyCopied)
cloned.setValue(clonedRelatedObject, forKey: key)
}
}
}
}
return cloned
}
}
使用方法:
let myManagedObjectCopy = myManagedObject.copyEntireObjectGraph(context: myContext)
我注意到当前答案中存在一些错误。首先,在迭代过程中,某些东西似乎正在改变与之相关的ToMany对象集合。其次,我不确定API是否发生了更改,但是在获取那些相关对象时,使用NSRelationshipDescription
的字符串表示形式作为键会引发异常。
我进行了一些微调,进行了一些基本测试,看起来可以工作。如果有人想进一步调查,那就太好了!
@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
[cloned setValue:[source valueForKey:relName] forKey:relName];
这段代码不会克隆目标对象中的单向关系,因此可能导致异常,因为您可能尝试链接来自不同上下文的对象。这也与toMany分支不一致。这很可能是一个拼写错误/小疏忽。 - Felix Lamouroux我修改了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
这样做可以吗?(未经测试)这将是您提到的“手动方式”,但它会自动与模型更改等同步,因此您不必手动输入所有属性名称。
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
我有一个真正的需要解决@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