Man*_*ani 54 cocoa core-data nsmanagedobject
我有一个托管对象("A"),它包含各种属性和关系类型,它的关系也有自己的属性和关系.我想做的是"复制"或"复制"以对象"A"为根的整个对象图,从而创建一个与"A"非常相似的新对象"B".
更具体地说,"B"(或其子代)所包含的关系都不应指向与"A"相关的对象.应该有一个完全相同的关系完整的新对象图,所有对象具有相同的属性,但当然不同的id.
有一种明显的手动方式可以做到这一点,但我希望了解一种更简单的方法,这在核心数据文档中并不完全明显.
TIA!
小智 57
这是我创建的一个类,用于执行托管对象的"深层复制":属性和关系.请注意,这不会检查对象图中的循环.(感谢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
Run Code Online (Sandbox Code Playgroud)
小智 42
这些答案让我非常接近,尽管他们似乎确实有一些缺点:
1,我接受了ZS的建议并将其作为NSManagedObject的一个类别,这对我来说似乎有点清洁.
2,我的对象图包含一对一的关系,所以我从levous的例子开始,但请注意,levous的例子不是在一对一关系的情况下克隆对象.这将导致崩溃(尝试从不同上下文中的一个上下文保存NSMO).我在下面的例子中解决了这个问题.
3,我提供了已经克隆的对象的缓存,这可以防止对象被克隆两次,因此在新的对象图中重复,并且还可以防止循环.
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
Run Code Online (Sandbox Code Playgroud)
lev*_*ous 24
我更新了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;
}
Run Code Online (Sandbox Code Playgroud)
mas*_*onk 16
这是@Derricks答案,修改通过询问关系,看它是否被命令支持新-AS-的的iOS 6.0下令一对多关系.虽然我在那里,我添加对于相同的NSManagedObjectContext内克隆的常见的情况更简单的方法-clone.
//
// 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
Run Code Online (Sandbox Code Playgroud)
斯威夫特 5
这建立在@Derrick & @Dmitry Makarenko & @masonk 的贡献之上,将所有内容整合在一起,对其进行改进并将其转变为适合 2020 年的解决方案。
.
import CoreData
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
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
let myManagedObjectCopy = myManagedObject.copyEntireObjectGraph(context: myContext)
Run Code Online (Sandbox Code Playgroud)
我注意到目前的答案有几个错误.首先,在迭代过程中似乎正在改变to-Many相关对象的集合.其次,我不确定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
Run Code Online (Sandbox Code Playgroud)
我修改了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
Run Code Online (Sandbox Code Playgroud)
像这样的东西?(未经测试)这将是您提到的“手动方式”,但它会自动与模型更改同步,因此您不必手动输入所有属性名称。
斯威夫特 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
}
}
Run Code Online (Sandbox Code Playgroud)
目标-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
Run Code Online (Sandbox Code Playgroud)
这将返回一个具有所有属性且没有复制关系的克隆。
小智 5
我真的需要解决@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
Run Code Online (Sandbox Code Playgroud)