NSMutableDictionary的深度可变副本

时间:2009-12-23 02:22:53

标签: objective-c iphone

我正在尝试创建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;
}
  • [onecopy release]应该在那里吗?
  • 以下是我将如何调用此方法:

    self.namesForAlphabets = [self.allNames mutableDeepCopy];

这样可以吗?还是会导致泄漏? (假设我将self.namesForAlphabets声明为属性,并在dealloc中释放它)。

8 个答案:

答案 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],则所有这些对象将被保留两次。

一个好的经验法则:如果您allocretaincopy某事,您还必须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

示例扩展 - 字符串保留为普通副本。如果您希望能够对其进行编辑,则可以覆盖此项。我只需要用深入的字典进行一些测试,所以我没有实现它。