Как сделать истинное глубокое копирование для NSArray и NSDictionary с вложенными массивами / словарь? - PullRequest
20 голосов
/ 28 марта 2011

Вопрос: есть ли способ использовать существующие методы target-c для создания полной глубокой копии NSDictionary или NSArray, в которые сами вложены словари или массивы?

То есть, я прочитал, что проблема может быть в том, что когда он попадает во вложенный словарь или массив, он только копирует указатель на вложенный элемент, а не копирует его по-настоящему.

Справочная информация. Поэтому в качестве примера я пытаюсь загрузить / сохранить следующую конфигурацию с помощью NSUserDefaults, и при загрузке необходимо преобразовать неизменяемые копии, полученные из NSUserDefault, в изменяемую до внесения изменений.

  • Предметы ( NSDictionary )
    • Item ( NSDictionary )
      • aString: NSString
      • aString2: NSString
      • Дата: NSDate
      • aDate2: NSDate
      • aBool: BOOL
      • aTI1: NSTimeInterval
      • aTI2: NSTimeInterval
      • Ключевые слова ( NSArray )
        • ключевое слово: NSString
        • ключевое слово: NSString

Ответы [ 4 ]

34 голосов
/ 28 марта 2011

Пару лет назад я написал по одной и той же причине несколько методов категории, преобразуя целое дерево пользовательских значений по умолчанию в изменяемые. Вот они - используйте их на свой страх и риск! : -)

//
//  SPDeepCopy.h
//
//  Created by Sherm Pendley on 3/15/09.
//

#import <Cocoa/Cocoa.h>

// Deep -copy and -mutableCopy methods for NSArray and NSDictionary

@interface NSArray (SPDeepCopy)

- (NSArray*) deepCopy;
- (NSMutableArray*) mutableDeepCopy;

@end

@interface NSDictionary (SPDeepCopy)

- (NSDictionary*) deepCopy;
- (NSMutableDictionary*) mutableDeepCopy;

@end

//
//  SPDeepCopy.m
//
//  Created by Sherm Pendley on 3/15/09.
//

#import "SPDeepCopy.h"


@implementation NSArray (SPDeepCopy)

- (NSArray*) deepCopy {
    unsigned int count = [self count];
    id cArray[count];

    for (unsigned int i = 0; i < count; ++i) {
        id obj = [self objectAtIndex:i];
        if ([obj respondsToSelector:@selector(deepCopy)])
            cArray[i] = [obj deepCopy];
        else
            cArray[i] = [obj copy];
    }

    NSArray *ret = [[NSArray arrayWithObjects:cArray count:count] retain];

    // The newly-created array retained these, so now we need to balance the above copies
    for (unsigned int i = 0; i < count; ++i)
        [cArray[i] release];

    return ret;
}
- (NSMutableArray*) mutableDeepCopy {
    unsigned int count = [self count];
    id cArray[count];

    for (unsigned int i = 0; i < count; ++i) {
        id obj = [self objectAtIndex:i];

        // Try to do a deep mutable copy, if this object supports it
        if ([obj respondsToSelector:@selector(mutableDeepCopy)])
            cArray[i] = [obj mutableDeepCopy];

        // Then try a shallow mutable copy, if the object supports that
        else if ([obj respondsToSelector:@selector(mutableCopyWithZone:)])
            cArray[i] = [obj mutableCopy];

        // Next try to do a deep copy
        else if ([obj respondsToSelector:@selector(deepCopy)])
            cArray[i] = [obj deepCopy];

        // If all else fails, fall back to an ordinary copy
        else
            cArray[i] = [obj copy];
    }

    NSMutableArray *ret = [[NSMutableArray arrayWithObjects:cArray count:count] retain];

    // The newly-created array retained these, so now we need to balance the above copies
    for (unsigned int i = 0; i < count; ++i)
        [cArray[i] release];

    return ret;
}

@end

@implementation NSDictionary (SPDeepCopy)

- (NSDictionary*) deepCopy {
    unsigned int count = [self count];
    id cObjects[count];
    id cKeys[count];

    NSEnumerator *e = [self keyEnumerator];
    unsigned int i = 0;
    id thisKey;
    while ((thisKey = [e nextObject]) != nil) {
        id obj = [self objectForKey:thisKey];

        if ([obj respondsToSelector:@selector(deepCopy)])
            cObjects[i] = [obj deepCopy];
        else
            cObjects[i] = [obj copy];

        if ([thisKey respondsToSelector:@selector(deepCopy)])
            cKeys[i] = [thisKey deepCopy];
        else
            cKeys[i] = [thisKey copy];

        ++i;
    }

    NSDictionary *ret = [[NSDictionary dictionaryWithObjects:cObjects forKeys:cKeys count:count] retain];

    // The newly-created dictionary retained these, so now we need to balance the above copies
    for (unsigned int i = 0; i < count; ++i) {
        [cObjects[i] release];
        [cKeys[i] release];
    }

    return ret;
}
- (NSMutableDictionary*) mutableDeepCopy {
    unsigned int count = [self count];
    id cObjects[count];
    id cKeys[count];

    NSEnumerator *e = [self keyEnumerator];
    unsigned int i = 0;
    id thisKey;
    while ((thisKey = [e nextObject]) != nil) {
        id obj = [self objectForKey:thisKey];

        // Try to do a deep mutable copy, if this object supports it
        if ([obj respondsToSelector:@selector(mutableDeepCopy)])
            cObjects[i] = [obj mutableDeepCopy];

        // Then try a shallow mutable copy, if the object supports that
        else if ([obj respondsToSelector:@selector(mutableCopyWithZone:)])
            cObjects[i] = [obj mutableCopy];

        // Next try to do a deep copy
        else if ([obj respondsToSelector:@selector(deepCopy)])
            cObjects[i] = [obj deepCopy];

        // If all else fails, fall back to an ordinary copy
        else
            cObjects[i] = [obj copy];

        // I don't think mutable keys make much sense, so just do an ordinary copy
        if ([thisKey respondsToSelector:@selector(deepCopy)])
            cKeys[i] = [thisKey deepCopy];
        else
            cKeys[i] = [thisKey copy];

        ++i;
    }

    NSMutableDictionary *ret = [[NSMutableDictionary dictionaryWithObjects:cObjects forKeys:cKeys count:count] retain];

    // The newly-created dictionary retained these, so now we need to balance the above copies
    for (unsigned int i = 0; i < count; ++i) {
        [cObjects[i] release];
        [cKeys[i] release];
    }

    return ret;
}

@end
31 голосов
/ 09 августа 2012

Я думаю, что-то вроде этого должно работать.

NSData *buffer;
NSMutableDictionary *_dict1, *_dict2;

// Deep copy "all" objects in _dict1 pointers and all to _dict2  
buffer = [NSKeyedArchiver archivedDataWithRootObject: _dict1];  
_dict2 = [NSKeyedUnarchiver unarchiveObjectWithData: buffer];  
23 голосов
/ 20 декабря 2012

Простой способ для DEEP-копии - просто использовать CFPropertyListCreateDeepCopy.Вот пример (с ARC):

    NSDictionary *newDictionary =
 (__bridge NSDictionary *)(CFPropertyListCreateDeepCopy(kCFAllocatorDefault,
         (__bridge CFPropertyListRef)(originalDictionary),
          kCFPropertyListImmutable));

мой originalDictionary в этом примере является NSDictionary

CFPropertyListRef может быть просто приведен к NSDictionary до тех пор, пока сущность верхнего уровня CFPropertyListRef являетсяCFDictionary.

0 голосов
/ 30 мая 2019

Если кому-то нужна изменяемая копия, пожалуйста, используйте следующий код:

NSMutableDictionary * newDictionary = (NSMutableDictionary *) CFBridgingRelease (CFPropertyListCreateDeepCopy (kCFAllocatorDefault, (CFDictionaryRef) dict, kM))

...