さまざまな属性と関係のタイプを含む管理対象オブジェクト(「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
これらの答えは私に本当に近づきましたが、いくつかの欠点があるようでした:
1つ目は、Z Sのアドバイスを受けてNSManagedObjectのカテゴリにしたことです。
2番目に、オブジェクトグラフには1対1の関係が含まれているため、levousの例から始めましたが、levousの例は1対1の関係の場合にオブジェクトのクローンを作成しないことに注意してください。これにより、クラッシュが発生します(NSMOを別のコンテキストの1つのコンテキストから保存しようとします)。以下の例で対処しました。
3番目に、すでに複製されたオブジェクトのキャッシュを提供しました。これにより、オブジェクトが2回複製されて新しいオブジェクトグラフに複製されるのを防ぎ、循環も防ぎます。
4番目に、ブラックリスト(クローンを作成しないエンティティタイプのリスト)を追加しました。これは、最終的な解決策の1つの欠点を解決するために部分的に行いました。これについては、以下で説明します。
注:私が理解していることをCoreDataのベストプラクティスとして使用し、常に逆の関係を提供する場合、これはおそらくクローンするオブジェクトとの関係を持つallオブジェクトを複製します。インバースを使用していて、他のすべてのオブジェクトについて知っている単一のルートオブジェクトがある場合、おそらくすべてを複製します。これに対する私の解決策は、ブラックリストを追加し、複製したいオブジェクトのいずれかの親であることがわかっているエンティティタイプを渡すことでした。これは私のために働くようです。 :)
ハッピークローニング!
// 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
ToOne関係をサポートするために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の新しいas-to-many関係をサポートするように変更され、関係を調べて順序付けられているかどうかを確認します。そこにいる間に、同じ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
現在の回答にはいくつかのバグがあります。第一に、反復されると、多対多の関連オブジェクトのセットが変化しているようです。第二に、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
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
このようなもの? (テストなし)これはあなたが言及する「手動の方法」ですが、モデルの変更などに自動的に同期するため、すべての属性名を手動で入力する必要はありません。
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はこの機能を提供しません。
ただし、多くの場合、必要性を回避するアーキテクチャがあります。オブジェクトグラフ全体のコピーを作成する代わりに、オブジェクトグラフをコピーして元のグラフのみを参照した場合に生じる差異(または将来の差異)をカプセル化する新しいエンティティを作成できます。つまり、新しい「カスタマイザー」エンティティをインスタンス化し、オブジェクトグラフ全体をコピーしないでください。たとえば、一連の長屋を考えます。それぞれに同一のフレーミングとアプライアンスがありますが、所有者はペイントと家具をカスタマイズできます。各所有者の家のグラフ全体を深くコピーする代わりに、所有者ごとに所有者と家のモデルを参照する「絵画と家具」エンティティを用意します。
ここに私のSwiftアプローチがあります:
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
}
これは「ディープコピー」と呼ばれます。それは驚くほど高価になる可能性があるため、多くの言語/ライブラリはそのままではサポートせず、独自にロールする必要があります。残念ながら、ココアはそれらの1つです。
関係階層のエンティティのみを関連付けたい場合は、次のコードをDmitryのソリューションに追加するだけです。
これの間に
NSString *entityName = [[self entity] name];
HERE if([namesOfEntitiesToExclude containsObject:entityName]){
NSMutableArray *arrayToOnlyRelate = [NSMutableArray arrayWithObjects:@"ENTITY 1",@"ENTITY 2",@"ENTITY 3", nil];
if ([arrayToOnlyRelate containsObject:entityName]) {
return self;
}
また:
[clone setValuesForKeysWithDictionary:[item dictionaryWithValuesForKeys:[properties allKeys]]];
[clone setValuesForKeysWithDictionary:[item dictionaryWithValuesForKeys:[attributes allKeys]]];
これに関する私の見解は https://Gist.github.com/jpmhouston/7958fceae9216f69178d4719a3492577 にあります
rel.inverseRelationship.name
を再帰的メソッドに渡し、alreadyCopied
オブジェクトのセットを維持するのではなく、逆関係へのアクセスを省略します
浅いコピーまたは深いコピー
notクローンとの関係の keypaths を受け入れますが、省略するか、逆の場合は単純にコピーします対多の関係
順序付けられた、多対多のリレーションシップの回避策-ソースエンティティを単純に反復する backwards :)これが良いアイデアなのか、それともすべてうまくいくのかわからない時間
特に、誰かが Benjohn の誤った順序に関するコメントについて詳しく説明できる場合は、フィードバックとコメントを歓迎します above "これを回避するには、順序付きセットを完了してから、プリミティブKVOバリアントを使用して割り当てます。 ".
また、私はMagicalRecordを使用しているので、デフォルトのコンテキストを使用する簡単なメソッドを提供するなど、私のコードはそれを想定しています。
Swift 4.0バージョン
import UIKit
import CoreData
class ManagedObjectCloner: NSObject {
static func cloneObject(source :NSManagedObject, context :NSManagedObjectContext) -> NSManagedObject{
let entityName = source.entity.name
let cloned = NSEntityDescription.insertNewObject(forEntityName: entityName!, into: context)
let attributes = NSEntityDescription.entity(forEntityName: entityName!, in: context)?.attributesByName
for (key,_) in attributes! {
cloned.setValue(source.value(forKey: key), forKey: key)
}
let relationships = NSEntityDescription.entity(forEntityName: entityName!, in: context)?.relationshipsByName
for (key,_) in relationships! {
let sourceSet = source.mutableSetValue(forKey: key)
let clonedSet = cloned.mutableSetValue(forKey: key)
let e = sourceSet.objectEnumerator()
var relatedObj = e.nextObject() as? NSManagedObject
while ((relatedObj) != nil) {
let clonedRelatedObject = ManagedObjectCloner.cloneObject(source: relatedObj!, context: context)
clonedSet.add(clonedRelatedObject)
relatedObj = e.nextObject() as? NSManagedObject
}
}
return cloned
}
}