Подтвердить что ты не робот

Как я могу дублировать или копировать управляемый объект с базовыми данными?

У меня есть управляемый объект ( "A" ), который содержит различные атрибуты и типы отношений, а его отношения также имеют свои собственные атрибуты и отношения. То, что я хотел бы сделать, это "скопировать" или "продублировать" весь граф объекта, внедренный в объект "A", и тем самым создать новый объект "B", который очень похож на "A".

Чтобы быть более конкретным, ни одно из отношений, содержащихся в "B" (или его дочерних элементах), не должно указывать на объекты, связанные с "A". Должен быть совершенно новый графический объект с одинаковыми отношениями, а все объекты имеют одинаковые атрибуты, но, конечно, разные идентификаторы.

Существует очевидный ручной способ сделать это, но я надеялся узнать о более простых способах сделать это, что не было полностью очевидно из документации Core Data.

ТИА!

4b9b3361

Ответ 1

Здесь класс, который я создал для выполнения "глубокой копии" управляемых объектов: атрибутов и отношений. Обратите внимание, что это не проверяет циклы объектов в графе объектов. (Спасибо 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

Ответ 2

Эти ответы заставили меня по-настоящему закрыть, хотя у них действительно были некоторые недостатки:

Впервые я принял совет Z S и сделал его категорией в NSManagedObject, это показалось мне немного более чистым.

2nd, My object graph содержит отношения "один", поэтому я начал с levous example, но обратите внимание, что levous пример не клонирует объект в случае отношения "один". Это приведет к сбою (попытка сохранить NSMO из одного контекста в другом контексте). Я обратился к этому в следующем примере.

В-третьих, я предоставил кэш уже клонированных объектов, это предотвращает клонирование объектов дважды и поэтому дублируется в новом графе объектов, а также предотвращает циклы.

В-четвертых, я добавил черный список (список типов Entity для клонирования). Я сделал это частично, чтобы решить один из недостатков моего окончательного решения, о котором я расскажу ниже.

ПРИМЕЧАНИЕ. Если вы используете то, что я понимаю, для лучшей практики CoreData, всегда предоставляя обратные отношения, тогда это, вероятно, будет клонировать все объекты, которые имеют отношение к объекту, который вы хотите клонировать. Если вы используете инверсии и у вас есть один корневой объект, который знает обо всех других объектах, то вы, скорее всего, клонируете все это. Моим решением было добавить черный список и передать в тип Entity, который, как я знал, был родителем одного из объектов, которые я хотел клонировать. Кажется, это работает для меня.:)

Счастливое клонирование!

// 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

Ответ 3

Я обновил user353759 ответ на поддержку toOne отношений.

@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;
}

Ответ 4

Это ответ @Derricks, измененный для поддержки новых контактов iOS 6.0, заказанных для многих, путем опроса отношения, чтобы узнать, упорядочено ли оно. В то время как я был там, я добавил более простой метод для обычного случая клонирования в пределах того же 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

Ответ 5

Я заметил пару ошибок с текущими ответами. Во-первых, что-то похоже на то, что мутирует множество объектов, связанных со многими, поскольку их повторение повторяется. Во-вторых, я не уверен, что что-то изменилось в API, но с использованием представления NSRelationshipDescription String в качестве ключа выбрасывались исключения при захвате этих связанных объектов.

Я сделал несколько настроек, провел базовое тестирование и, похоже, работает. Если кто-то хочет исследовать дальше, это было бы здорово!

@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

Ответ 6

Я изменил ответ 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

Ответ 7

Что-то вроде этого? (непроверенный). Это будет "ручной способ", который вы упомянули, но он будет автоматически синхронизироваться с изменениями модели и поэтому вам не нужно вручную вводить все имена атрибутов.

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

Это вернет вам клон со всеми атрибутами и отношения не будут скопированы.

Ответ 8

У меня возникла настоящая необходимость обойти проблему массового копирования, которую @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

Ответ 9

То, что вы просите, называется "глубокой копией". Поскольку это может быть очень дорого (как при неограниченном использовании памяти) и очень сложно получить право (рассмотрите петли в графе объектов), Core Data не предоставит вам эту возможность.

Часто существует архитектура, которая позволяет избежать необходимости. Вместо того, чтобы делать копию всего графа объектов, возможно, вы можете создать новый объект, который инкапсулирует различия (или будущие различия), которые вы имели бы, если бы скопировали граф объектов и затем ссылались только на исходный граф. Другими словами, создайте экземпляр нового объекта "customizer" и не скопируйте весь граф объекта. Например, рассмотрим набор рядовых домов. Каждый из них имеет одинаковое оснащение и приборы, но владелец может настроить краску и мебель. Вместо глубокого копирования всего графа дома для каждого владельца, есть объект "живопись и мебель", который ссылается на владельца и модель дома - для каждого владельца.

Ответ 11

Это называется "глубокой копией". Поскольку это может быть удивительно дорого, многие языки/библиотеки не поддерживают его из коробки и требуют, чтобы вы катили свой собственный. Cocoa, к сожалению, один из них.

Ответ 12

Здесь у вас есть мой подход swift 3:

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
    }

Ответ 13

также:

[clone setValuesForKeysWithDictionary:[item dictionaryWithValuesForKeys:[properties allKeys]]];
[clone setValuesForKeysWithDictionary:[item dictionaryWithValuesForKeys:[attributes allKeys]]];

Ответ 14

Если вы хотите связывать только сущности в иерархии отношений, вам нужно добавить следующий код к решению Дмитрия

Между этим

NSString *entityName = [[self entity] name];

ЗДЕСЬ   if ([namesOfEntitiesToExclude containsObject: entityName]) {

NSMutableArray *arrayToOnlyRelate   = [NSMutableArray arrayWithObjects:@"ENTITY 1",@"ENTITY 2",@"ENTITY 3", nil];

if ([arrayToOnlyRelate containsObject:entityName]) {
    return self;
}

Ответ 15

Я беру это на https://gist.github.com/jpmhouston/7958fceae9216f69178d4719a3492577

  • передает rel.inverseRelationship.name в рекурсивный метод, чтобы не посещать обратные отношения, а не поддерживать набор объектов alreadyCopied

  • мелкие или глубокие копии

  • принимает ключевые пути отношений к не клону, но либо опускать, либо просто копировать, если обратное является отношением to-many

  • обходной путь для упорядоченных отношений со многими, заканчивающихся в обратном порядке - просто перебирайте исходные сущности назад:) Я не уверен, что это хорошая идея или если она даже работает все время

Обратная связь и комментарии приветствуются, особенно если кто-то может прокомментировать Benjohn комментарий о неправильном заказе выше "Работа для этого состоит в том, чтобы построить полный упорядоченный набор, а затем назначить с использованием примитивного варианта KVO". и может улучшить мое упорядоченное, к-многим обходное решение.

Кроме того, я использую MagicalRecord, поэтому мой код предполагает, что, включая предоставление простых методов, которые используют свой контекст по умолчанию.