0

I'm using CFPropertyListCreateDeepCopy to make a deep mutable copy of an NSDictionary. This example works fine.

NSDictionary *test = @{@"1": @"One"};
NSMutableDictionary *dictionary = (__bridge NSMutableDictionary *)CFPropertyListCreateDeepCopy(NULL, (__bridge CFDictionaryRef)test, kCFPropertyListMutableContainersAndLeaves);

This doesn't work when the NSDictionary uses an NSNumber for a key value. CFPropertyListCreateDeepCopy returns nil. Here is an example.

NSDictionary *test = @{@(1): @"One"};
NSMutableDictionary *dictionary = (__bridge NSMutableDictionary *)CFPropertyListCreateDeepCopy(NULL, (__bridge CFDictionaryRef)test, kCFPropertyListMutableContainersAndLeaves);

How do I make a deep copy of an NSDictionary that has an NSNumber as a key value?

Berry Blue
  • 15,330
  • 18
  • 62
  • 113
  • While it's fine to use any type of object for keys in an `NSDictionary`, property lists only support keys of type `NSString`. – rmaddy Apr 10 '16 at 03:48
  • 1
    You may wish to look at http://stackoverflow.com/questions/5453481/how-to-do-true-deep-copy-for-nsarray-and-nsdictionary-with-have-nested-arrays-di?s=3|0.4875 The 2nd answer is simple and clever. – rmaddy Apr 10 '16 at 03:51

1 Answers1

1

And although NSDictionary and CFDictionary objects allow their keys to be objects of any type, if the keys are not string objects, the collections are not property-list objects.

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/PropertyLists/AboutPropertyLists/AboutPropertyLists.html#//apple_ref/doc/uid/10000048i-CH3-54303

You cannot copy a property list with a non-string key, because this is no property list.

Simply iterate over the dictionary and copy the items manually.

@implemenatation NSDictionary (DeepCopy)
- (NSMutableDictionary*)deepMutableCopy
{
  NSMutableDictionary *copy = [NSMutableDictionary new];
  [self enumerateKeysAndObjectsUsingBlock
  ^(id key, id object)
  {
    if([object respondsToSelector:@selector(deepMutableCopy)])
    {
      object = [object deepMutableCopy];
    }
    else if ([object respondsToSelector:@selector(mutableCopy)])
    {
      object = [object mutableCopy];
    }
    else if ([object respondsToSelector:@selector(copy)]) // Maybe, maybe not
    {
      object = [object copy];
    }
    [copy setObject:object forKey:key]
  }];
  return copy;
}

Do the same with NSArray.

(For such tasks I would like to have components in Objective-C.)

Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50
  • It's not simple if the dictionary contains other collections. Clearly the example in the question is an overly simple example otherwise you can simply call `mutableCopy` on the original dictionary. – rmaddy Apr 10 '16 at 04:02
  • You need simple categories on `NSDictionary` and `NSArray`. I do not think that this is more than 20 lines of code. I add this to my answer. – Amin Negm-Awad Apr 10 '16 at 04:33
  • Do the same with the other collection classes. Change `addObject:forKey:` to `setObject:forKey:` in the sample code. – Willeke Apr 10 '16 at 12:11
  • Other? There are no other property list collections beside `NSDictionary` and `NSArray`!? – Amin Negm-Awad Apr 10 '16 at 16:42