web-dev-qa-db-ja.com

ネストされた配列/辞書を持つNSArrayとNSDictionaryの真のディープコピーを行う方法は?

質問:既存のobjective-cメソッドを使用して、ネストされた辞書または配列が含まれているNSDictionaryまたはNSArrayの完全なディープコピーを作成する方法はありますか?

つまり、ネストされた辞書または配列にヒットすると、ネストされたアイテムへのポインタのみがコピーされ、アイテムが実際にはコピーされない可能性があることを読みました。

背景:私にとっての例として、NSUserDefaultsを使用して次の構成をロード/保存しようとしています。ロード時に、変更を加える前に、NSUserDefaultから取得した不変のコピーを変更可能に変換する必要があります。

  • アイテム(NSDictionary
    • アイテム(NSDictionary
      • aString:NSString
      • aString2:NSString
      • aDate:NSDate
      • aDate2:NSDate
      • aBool:BOOL
      • ATI1:NSTimeInterval
      • ATI2:NSTimeInterval
      • キーワード(NSArray
        • キーワード:NSString
        • キーワード:NSString
21
Greg

数年前、私はまったく同じ理由でいくつかのカテゴリメソッドを作成し、ユーザーのデフォルトのツリー全体を変更可能に変換しました。ここにあります-あなた自身の責任でそれらを使用してください! :-)

//
//  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
34
Sherm Pendley

私はこのようなものがうまくいくはずだと思います。

NSData *buffer;
NSMutableDictionary *_dict1, *_dict2;

// Deep copy "all" objects in _dict1 pointers and all to _dict2  
buffer = [NSKeyedArchiver archivedDataWithRootObject: _dict1];  
_dict2 = [NSKeyedUnarchiver unarchiveObjectWithData: buffer];  
31
RickR

DEEPコピーの簡単な方法は、CFPropertyListCreateDeepCopyを使用することです。次に例を示します(ARCを使用)。

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

この例の私のoriginalDictionaryはNSDictionaryです

CFPropertyListRefの最上位エンティティがCFDictionaryである限り、CFPropertyListRefは単純にNSDictionaryにキャストできます。

23
dbainbridge

誰かが可変コピーが必要な場合は、以下のコードを使用してください:

NSMutableDictionary * newDictionary =(NSMutableDictionary *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault、(CFDictionaryRef)dict、kCFPropertyListMutableContainers));

0
Vikram Sinha