我正在尝试创建NSMutableDictionary的深层副本并将其分配给另一个NSMutableDictionary。字典包含一堆数组,每个数组包含名称,键是字母表(这些名称的第一个字母)。因此字典中的一个条目是'A' - > '亚当','苹果'。这是我在书中看到的内容,但我不确定它是否有效:
- (NSMutableDictionary *) mutableDeepCopy
{
NSMutableDictionary * ret = [[NSMutableDictionary alloc] initWithCapacity: [self count]];
NSArray *keys = [self allKeys];
for (id key in keys)
{
id oneValue = [self valueForKey:key]; // should return the array
id oneCopy = nil;
if ([oneValue respondsToSelector: @selector(mutableDeepCopy)])
{
oneCopy = [oneValue mutableDeepCopy];
}
if ([oneValue respondsToSelector:@selector(mutableCopy)])
{
oneCopy = [oneValue mutableCopy];
}
if (oneCopy == nil) // not sure if this is needed
{
oneCopy = [oneValue copy];
}
[ret setValue:oneCopy forKey:key];
//[oneCopy release];
}
return ret;
}
以下是我将如何调用此方法:
self.namesForAlphabets = [self.allNames mutableDeepCopy];
这样可以吗?还是会导致泄漏? (假设我将self.namesForAlphabets声明为属性,并在dealloc中释放它)。
答案 0 :(得分:73)
由于免费桥接,您还可以使用CoreFoundation函数CFPropertyListCreateDeepCopy
:
NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);
答案 1 :(得分:11)
假设阵列的所有元素都实现了NSCoding协议,您可以通过归档来执行深层复制,因为归档将保留对象的可变性。
这样的事情:
id DeepCopyViaArchiving(id<NSCoding> anObject)
{
NSData* archivedData = [NSKeyedArchiver archivedDataWithRootObject:anObject];
return [[NSKeyedUnarchiver unarchiveObjectWithData:archivedData] retain];
}
但这并不是特别有效。
答案 2 :(得分:9)
重要:问题(以及我的代码)都处理了一个非常具体的案例,其中NSMutableDictionary包含仅字符串数组。对于更复杂的示例,这些解决方案将不起作用。有关更一般的案例解决方案,请参阅以下内容:
回答这个具体案例:
您的代码应该有效,但您肯定需要[oneCopy release]
。当您使用setValue:forKey
添加复制对象时,新词典将保留复制的对象,因此如果您不调用[oneCopy release]
,则所有这些对象将被保留两次。
一个好的经验法则:如果您alloc
,retain
或copy
某事,您还必须release
。
注意:这里有一些示例代码仅适用于某些情况的 。这是有效的,因为您的NSMutableDictionary仅包含字符串数组(不需要进一步深度复制):
- (NSMutableDictionary *)mutableDeepCopy
{
NSMutableDictionary * ret = [[NSMutableDictionary alloc]
initWithCapacity:[self count]];
NSMutableArray * array;
for (id key in [self allKeys])
{
array = [(NSArray *)[self objectForKey:key] mutableCopy];
[ret setValue:array forKey:key];
[array release];
}
return ret;
}
答案 3 :(得分:8)
我见过的另一种技术(根本不是非常有效)是使用NSPropertyListSerialization
对象来序列化你的字典,然后你对它进行反序列化,但指定你想要可变的叶子和容器。 / p>
NSString *errorString = nil;
NSData *binData =
[NSPropertyListSerialization dataFromPropertyList:self.allNames
format:NSPropertyListBinaryFormat_v1_0
errorString:&errorString];
if (errorString) {
// Something bad happened
[errorString release];
}
self.namesForAlphabets =
[NSPropertyListSerialization propertyListFromData:binData
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:NULL
errorDescription:&errorString];
if (errorString) {
// something bad happened
[errorString release];
}
同样,这根本没有效率。
答案 4 :(得分:4)
尝试通过选中respondToSelector(@selector(mutableCopy))
来确定不会产生所需的结果,因为所有基于NSObject
的对象都会响应此选择器(它是NSObject
的一部分)。相反,我们必须查询对象是否符合NSMutableCopying
或至少NSCopying
。这是我的回答基于接受的答案中提到的this gist:
NSDictionary
:
@implementation NSDictionary (MutableDeepCopy)
// As seen here (in the comments): https://gist.github.com/yfujiki/1664847
- (NSMutableDictionary *)mutableDeepCopy
{
NSMutableDictionary *returnDict = [[NSMutableDictionary alloc] initWithCapacity:self.count];
NSArray *keys = [self allKeys];
for(id key in keys) {
id oneValue = [self objectForKey:key];
id oneCopy = nil;
if([oneValue respondsToSelector:@selector(mutableDeepCopy)]) {
oneCopy = [oneValue mutableDeepCopy];
} else if([oneValue conformsToProtocol:@protocol(NSMutableCopying)]) {
oneCopy = [oneValue mutableCopy];
} else if([oneValue conformsToProtocol:@protocol(NSCopying)]){
oneCopy = [oneValue copy];
} else {
oneCopy = oneValue;
}
[returnDict setValue:oneCopy forKey:key];
}
return returnDict;
}
@end
NSArray
:
@implementation NSArray (MutableDeepCopy)
- (NSMutableArray *)mutableDeepCopy
{
NSMutableArray *returnArray = [[NSMutableArray alloc] initWithCapacity:self.count];
for(id oneValue in self) {
id oneCopy = nil;
if([oneValue respondsToSelector:@selector(mutableDeepCopy)]) {
oneCopy = [oneValue mutableDeepCopy];
} else if([oneValue conformsToProtocol:@protocol(NSMutableCopying)]) {
oneCopy = [oneValue mutableCopy];
} else if([oneValue conformsToProtocol:@protocol(NSCopying)]){
oneCopy = [oneValue copy];
} else {
oneCopy = oneValue;
}
[returnArray addObject:oneCopy];
}
return returnArray;
}
@end
这两种方法都具有相同的内部复制或不复制逻辑,可以将其提取到单独的方法中,但为了清楚起见,我将其保留为此。
答案 5 :(得分:1)
如果您使用ARC,我想我会更新答案。
Weva提供的解决方案效果很好。现在你可以这样做:
NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDict, kCFPropertyListMutableContainers));
答案 6 :(得分:1)
对于ARC - 注意kCFPropertyListMutableContainersAndLeaves真正的深度可变性。
NSMutableDictionary* mutableDict = (NSMutableDictionary *)
CFBridgingRelease(
CFPropertyListCreateDeepCopy(kCFAllocatorDefault,
(CFDictionaryRef)someNSDict,
kCFPropertyListMutableContainersAndLeaves));
答案 7 :(得分:0)
这里有用的答案,但CFPropertyListCreateDeepCopy
不处理数据中的[NSNull null]
,例如,这对于JSON解码数据来说非常正常。
我正在使用此类别:
#import <Foundation/Foundation.h>
@interface NSObject (ATMutableDeepCopy)
- (id)mutableDeepCopy;
@end
实施(随意改变/扩展):
@implementation NSObject (ATMutableDeepCopy)
- (id)mutableDeepCopy
{
return [self copy];
}
@end
#pragma mark - NSDictionary
@implementation NSDictionary (ATMutableDeepCopy)
- (id)mutableDeepCopy
{
return [NSMutableDictionary dictionaryWithObjects:self.allValues.mutableDeepCopy
forKeys:self.allKeys.mutableDeepCopy];
}
@end
#pragma mark - NSArray
@implementation NSArray (ATMutableDeepCopy)
- (id)mutableDeepCopy
{
NSMutableArray *const mutableDeepCopy = [NSMutableArray new];
for (id object in self) {
[mutableDeepCopy addObject:[object mutableDeepCopy]];
}
return mutableDeepCopy;
}
@end
#pragma mark - NSNull
@implementation NSNull (ATMutableDeepCopy)
- (id)mutableDeepCopy
{
return self;
}
@end
示例扩展 - 字符串保留为普通副本。如果您希望能够对其进行编辑,则可以覆盖此项。我只需要用深入的字典进行一些测试,所以我没有实现它。