Skip to content

Instantly share code, notes, and snippets.

@yfujiki
Created January 23, 2012 18:48
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save yfujiki/1664847 to your computer and use it in GitHub Desktop.
Save yfujiki/1664847 to your computer and use it in GitHub Desktop.
Deep mutable copy category method of NSDictionary (ObjC)
- (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 respondsToSelector:@selector(mutableCopy)]) {
oneCopy = [oneValue mutableCopy];
}
else {
oneCopy = [oneValue copy];
}
[returnDict setValue:oneValue forKey:key];
}
return returnDict;
}
@trapsignal
Copy link

Maybe you wanted to add oneCopy to returnDict, not oneValue (line 18)?

- (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 respondsToSelector:@selector(mutableCopy)]) {
            oneCopy = [oneValue mutableCopy];
        }
        else {
            oneCopy = [oneValue copy];
        }
        [returnDict setValue:oneCopy forKey:key];
    }
    return returnDict;
}

@ierceg
Copy link

ierceg commented Nov 7, 2014

This doesn't work for all cases as all NSObject-based objects respond to mutableCopy selector. Instead, this needs to be done:

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

To do the same thing for arrays:

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

@wooknight
Copy link

@VasilyIvanov

Excellent catch. Solved my problem

@gentwo
Copy link

gentwo commented Jan 27, 2016

Collating all the above comments so it can be dropped in to code. Added a MutableDeepCopying protocol, so other objects can adopt it if needed.

// Header
@protocol MutableDeepCopying <NSObject>
-(id) mutableDeepCopy;
@end
@interface NSDictionary (MutableDeepCopy) <MutableDeepCopying>
@end
@interface NSArray (MutableDeepCopy) <MutableDeepCopying>
@end

// Implementation
@implementation NSDictionary (MutableDeepCopy)
- (NSMutableDictionary *) mutableDeepCopy {
  NSMutableDictionary * returnDict = [[NSMutableDictionary alloc] initWithCapacity:self.count];
  NSArray * keys = [self allKeys];
  for(id key in keys) {
    id aValue = [self objectForKey:key];
    id theCopy = nil;
    if([aValue conformsToProtocol:@protocol(MutableDeepCopying)]) {
      theCopy = [aValue mutableDeepCopy];
    } else if([aValue conformsToProtocol:@protocol(NSMutableCopying)]) {
      theCopy = [aValue mutableCopy];
    } else if([aValue conformsToProtocol:@protocol(NSCopying)]){
      theCopy = [aValue copy];
    } else {
      theCopy = aValue;
    }
    [returnDict setValue:theCopy forKey:key];
  }
  return returnDict;
}
@end

@implementation NSArray (MutableDeepCopy)
-(NSMutableArray *)mutableDeepCopy {
  NSMutableArray *returnArray = [[NSMutableArray alloc] initWithCapacity:self.count];
  for(id aValue in self) {
    id theCopy = nil;
    if([aValue conformsToProtocol:@protocol(MutableDeepCopying)]) {
      theCopy = [aValue mutableDeepCopy];
    } else if([aValue conformsToProtocol:@protocol(NSMutableCopying)]) {
      theCopy = [aValue mutableCopy];
    } else if([aValue conformsToProtocol:@protocol(NSCopying)]){
      theCopy = [aValue copy];
    } else {
      theCopy = aValue;
    }
    [returnArray addObject:theCopy];
  }
  return returnArray;
}
@end

@Motti-Shneor
Copy link

This is so nice! Could it be even nicer, to support the equivalent of mutability-options in NSPropertyListSerialization? i.e,

typedef NS_OPTIONS(NSUInteger, NSPropertyListMutabilityOptions) {
NSPropertyListImmutable = kCFPropertyListImmutable,
NSPropertyListMutableContainers = kCFPropertyListMutableContainers,
NSPropertyListMutableContainersAndLeaves = kCFPropertyListMutableContainersAndLeaves
};

so mutableDeepCopy method can receive the option, and selectively make (deep) mutable copies only to containers, but NOT to the "leaves" (e.g. NSData, NSString, NSDate, NSNumber etc. ? What's the best way to go about it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment