質問:既存のobjective-cメソッドを使用して、ネストされた辞書または配列が含まれているNSDictionaryまたはNSArrayの完全なディープコピーを作成する方法はありますか?
つまり、ネストされた辞書または配列にヒットすると、ネストされたアイテムへのポインタのみがコピーされ、アイテムが実際にはコピーされない可能性があることを読みました。
背景:私にとっての例として、NSUserDefaultsを使用して次の構成をロード/保存しようとしています。ロード時に、変更を加える前に、NSUserDefaultから取得した不変のコピーを変更可能に変換する必要があります。
数年前、私はまったく同じ理由でいくつかのカテゴリメソッドを作成し、ユーザーのデフォルトのツリー全体を変更可能に変換しました。ここにあります-あなた自身の責任でそれらを使用してください! :-)
//
// 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
私はこのようなものがうまくいくはずだと思います。
NSData *buffer;
NSMutableDictionary *_dict1, *_dict2;
// Deep copy "all" objects in _dict1 pointers and all to _dict2
buffer = [NSKeyedArchiver archivedDataWithRootObject: _dict1];
_dict2 = [NSKeyedUnarchiver unarchiveObjectWithData: buffer];
DEEPコピーの簡単な方法は、CFPropertyListCreateDeepCopyを使用することです。次に例を示します(ARCを使用)。
NSDictionary *newDictionary =
(__bridge NSDictionary *)(CFPropertyListCreateDeepCopy(kCFAllocatorDefault,
(__bridge CFPropertyListRef)(originalDictionary),
kCFPropertyListImmutable));
この例の私のoriginalDictionaryはNSDictionaryです
CFPropertyListRefの最上位エンティティがCFDictionaryである限り、CFPropertyListRefは単純にNSDictionaryにキャストできます。
誰かが可変コピーが必要な場合は、以下のコードを使用してください:
NSMutableDictionary * newDictionary =(NSMutableDictionary *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault、(CFDictionaryRef)dict、kCFPropertyListMutableContainers));