У меня есть управляемый объект ("A"), который содержит различные атрибуты и типы отношений, и его отношения также имеют свои собственные атрибуты и отношения. То, что я хотел бы сделать, должно "скопировать" или "копировать" весь граф объектов, базированный в объекте "A", и таким образом создание нового объекта "B", который очень похож на "A".
Чтобы быть точнее, ни одно из отношений, содержавших "B" (или его дети), не должно указывать на объекты, связанные с "A". Должен быть совершенно новый граф объектов с подобными неповрежденными отношениями, и все объекты, имеющие те же атрибуты, но конечно другой идентификатор.
Существует очевидный ручной способ сделать это, но я надеялся узнать о более простом средстве выполнения поэтому, которое не было полностью очевидно из Базовой документации Данных.
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
То, что вы просите, называется «глубокой копией». Поскольку это может быть очень дорогостоящим (например, при неограниченном использовании памяти) и очень сложным для правильного выполнения (учитывайте циклы в графе объектов), Core Data не предоставляет вам эту возможность.
Однако часто существует архитектура, в которой нет необходимости. Вместо того, чтобы делать копию всего графа объекта, возможно, вы можете создать новую сущность, которая инкапсулирует различия (или будущие различия), которые у вас были бы, если бы вы скопировали граф объекта, а затем ссылались бы только на исходный граф. Другими словами, создайте экземпляр новой сущности «настройщика» и не копируйте весь граф объекта. Например, рассмотрим набор рядных домов. Каждый из них имеет идентичный каркас и технику, но владелец может настроить краску и мебель. Вместо того чтобы копировать всю диаграмму дома для каждого владельца, создайте объект «картина и мебель» - который ссылается на владельца и модель дома - для каждого владельца.
Что-то вроде этого? (непроверено) Это будет упомянутый вами «ручной способ», но он будет автоматически синхронизироваться с изменениями модели и т.п., поэтому вам не придется вручную вводить все имена атрибутов.
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
Это вернет вам клон со всеми атрибутами и без скопированных отношений.
Это называется "глубокая копия". Поскольку это может быть удивительно дорого, многие языки/библиотеки не поддерживают его из коробки и требуют, чтобы вы создали свой собственный. Cocoa, к сожалению, один из них.
Также:
[clone setValuesForKeysWithDictionary:[item dictionaryWithValuesForKeys:[properties allKeys]]];
[clone setValuesForKeysWithDictionary:[item dictionaryWithValuesForKeys:[attributes allKeys]]];