deep mutable copy of a NSMutableDictionary

前端 未结 8 1874
醉话见心
醉话见心 2020-11-30 00:55

I am trying to create a deep-copy of a NSMutableDictionary and assign it to another NSMutableDictionary. The dictionary contains a bunch of arrays, each array containing nam

相关标签:
8条回答
  • 2020-11-30 01:06

    Assuming all elements of the array implement the NSCoding protocol, you can do deep copies via archiving because archiving will preserve the mutability of objects.

    Something like this:

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

    This isn't particularly efficient, though.

    0 讨论(0)
  • 2020-11-30 01:14

    IMPORTANT: The question (and my code below) both deal with a very specific case, in which the NSMutableDictionary contains only arrays of strings. These solutions will not work for more complex examples. For more general case solutions, see the following:

    • Tom Dalling's answer
    • dreamlax's answer
    • Source from yfujiki on GitHub Gist

    Answer for this specific case:

    Your code should work, but you will definitely need the [oneCopy release]. The new dictionary will retain the copied objects when you add them with setValue:forKey, so if you do not call [oneCopy release], all of those objects will be retained twice.

    A good rule of thumb: if you alloc, retain or copy something, you must also release it.

    Note: here is some sample code that would work for certain cases only. This works because your NSMutableDictionary contains only arrays of strings (no further deep copying required):

    - (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;
    }
    
    0 讨论(0)
  • 2020-11-30 01:15

    Useful answers here, but CFPropertyListCreateDeepCopy doesn't handle [NSNull null] in the data, which is pretty normal with JSON decoded data, for example.

    I'm using this category:

        #import <Foundation/Foundation.h>
    
        @interface NSObject (ATMutableDeepCopy)
        - (id)mutableDeepCopy;
        @end
    

    Implementation (feel free to alter / extend):

        @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
    

    Example extensions – strings are left as normal copies. You could override this if you want to be able to in place edit them. I only needed to monkey with a deep down dictionary for some testing, so I've not implemented that.

    0 讨论(0)
  • 2020-11-30 01:16

    Another technique that I have seen (which is not at all very efficient) is to use an NSPropertyListSerialization object to serialise your dictionary, then you de-serialise it but specify that you want mutable leaves and containers.

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

    Again, this is not at all efficient.

    0 讨论(0)
  • 2020-11-30 01:19

    Because of toll-free bridging, you can also use the CoreFoundation function CFPropertyListCreateDeepCopy:

    NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);
    
    0 讨论(0)
  • 2020-11-30 01:19

    Thought I'd update with an answer if you're using ARC.

    The solution Weva has provided works just fine. Nowadays you could do it like this:

    NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDict, kCFPropertyListMutableContainers));
    
    0 讨论(0)
提交回复
热议问题