代码之家  ›  专栏  ›  技术社区  ›  Z S

NSmutabledictionary的深度可变副本

  •  36
  • Z S  · 技术社区  · 15 年前

    我试图创建一个nsmutabledictionary的深度副本,并将其分配给另一个nsmutabledictionary。字典包含一组数组,每个数组包含名称,键是一个字母表(这些名称的第一个字母)。所以字典中的一个条目是'a'->'adam'、'apple'。这是我在一本书中看到的,但我不确定它是否有效:

    - (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版本]是否存在?
    • 以下是我将如何调用此方法:

      self.namesForAlphabets = [self.allNames mutableDeepCopy];

    这样可以吗?还是会引起泄漏?(假设我声明self.namesforalphabets为属性,并在dealoc中释放它)。

    8 回复  |  直到 8 年前
        1
  •  9
  •   Community miroxlav    7 年前

    重要的 :问题(以及下面我的代码)都处理一个非常具体的情况,其中nsmutabledictionary包含 只有 字符串数组。这些解决方案 不会工作 更复杂的例子。有关更一般的案例解决方案,请参见以下内容:


    具体案例的答案:

    您的代码应该可以工作,但是您肯定需要 [oneCopy release] . 新字典将在添加复制的对象时保留这些对象 setValue:forKey 如果你不打电话 [一份副本发布] ,所有这些对象将保留两次。

    一条很好的经验法则:如果你 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;
    }
    
        2
  •  72
  •   Wevah    13 年前

    由于免费桥接,您还可以使用corefoundation函数 CFPropertyListCreateDeepCopy :

    NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);
    
        3
  •  11
  •   Tom Dalling    15 年前

    假设数组的所有元素都实现了nscoding协议,您可以通过存档来进行深度复制,因为存档将保留对象的可变性。

    像这样:

    id DeepCopyViaArchiving(id<NSCoding> anObject)
    {
        NSData* archivedData = [NSKeyedArchiver archivedDataWithRootObject:anObject];
        return [[NSKeyedUnarchiver unarchiveObjectWithData:archivedData] retain];
    }
    

    不过,这并不是特别有效。

        4
  •  8
  •   Alex Cio Olie    9 年前

    我看到的另一种技术(一点也不高效)是使用 NSPropertyListSerialization 对象对字典进行序列化,然后对其进行反序列化,但指定需要可变的叶和容器。

    
    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];
    }
    

    再说一次,这一点效率都不高。

        5
  •  3
  •   ierceg    10 年前

    试图通过检查找出答案 respondToSelector(@selector(mutableCopy)) 不会得到所有想要的结果 NSObject -基于对象响应此选择器(它是 NSO对象 )相反,我们必须查询对象是否符合 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
    

    这两种方法都有相同的内部复制或不复制逻辑,可以将其提取到单独的方法中,但为了清晰起见,我将其保留为这样。

        6
  •  1
  •   johan    9 年前

    我想如果你使用ARC,我会更新一个答案。

    WEVA提供的解决方案工作得很好。现在你可以这样做:

    NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDict, kCFPropertyListMutableContainers));
    
        7
  •  1
  •   Tom Andersen    9 年前

    请注意,kcfpropertiesylistmutablecontainers和leaves具有真正的深度可变性。

        NSMutableDictionary* mutableDict = (NSMutableDictionary *)
          CFBridgingRelease(
              CFPropertyListCreateDeepCopy(kCFAllocatorDefault, 
               (CFDictionaryRef)someNSDict, 
               kCFPropertyListMutableContainersAndLeaves));
    
        8
  •  0
  •   Benjohn    8 年前

    这里是有用的答案,但是 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
    

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