これらの答えは私を本当に近づけましたが、いくつかの欠点があるように見えました:
まず、ZSのアドバイスを受けて、NSManagedObjectのカテゴリにしました。これは、私には少しわかりやすいように思えました。
2つ目は、オブジェクトグラフにto-one関係が含まれているため、levousの例から始めましたが、to-one関係の場合、levousの例はオブジェクトのクローンを作成していないことに注意してください。これにより、クラッシュが発生します(あるコンテキストから別のコンテキストでNSMOを保存しようとします)。以下の例でこれに対処しました。
3番目に、既に複製されたオブジェクトのキャッシュを提供しました。これにより、オブジェクトが2回複製されて新しいオブジェクトグラフに複製されるのを防ぎ、サイクルも防ぎます。
4番目に、ブラックリスト(複製しないエンティティタイプのリスト)を追加しました。これは、最終的な解決策の1つの欠点を解決するために部分的に行いました。これについては、以下で説明します。
注:私が理解しているCoreDataのベストプラクティスを使用し、常に逆の関係を提供する場合、これにより、複製するオブジェクトとの関係を持つすべてのオブジェクトが複製される可能性があります。インバースを使用していて、他のすべてのオブジェクトを認識している単一のルートオブジェクトがある場合は、すべてのクローンを作成する可能性があります。これに対する私の解決策は、ブラックリストを追加し、クローンを作成したいオブジェクトの1つの親であることがわかっているエンティティタイプを渡すことでした。これは私にとってはうまくいくようです。:)
ハッピークローン!
// 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