Mutable Deep Copy By Using Categories

Ever run into this error:

mutating method sent to immutable object

The above error means that the program has tried to modify an immutable object. No problem you think. I’ll just make the object mutable. Now the normal way to deal with this is to do:

[obj mutableCopy];

Depending on the calling object, this call returns a mutable copy of the primitive or the objects of an array or hash.

deep-mutable-copy-successful

Great, solution solved! Press the easy button and go to the next problem.

Still reading… No worky?

If you are still reading this then I guess the easy solution did not work for you. If the data structure in question is comprised of deep objects such as trees, JSON objects or other recursive structures you need something more.

Mutable Deep Copy will clone the data structure recursively and allow updating of contained objects no matter how deep the structure.

deep-mutable-copy-fail

In Objective-C, we can attach methods to existing classes via Categories. For deep mutable copy, We attach categories upon the following classes:

  • NSObject
  • NSArray
  • NSDictionary
  • NSSet

This should handle arrays, dictionaries, sets and most primitive objects but one will still need to implement the mutableDeepCopy method on your own custom classes if they are used in the data structure.

Add the following two files into your Xcode project and include them when you need to clone deep recursive structures.

DeepMutableCopy.h

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

DeepMutableCopy.m

#import "DeepMutableCopy.h"
@implementation NSObject (MutableDeepCopy)
- mutableDeepCopy
{
    if([self respondsToSelector:@selector(mutableCopyWithZone:)])
        return [self mutableCopy];
    else if([self respondsToSelector:@selector(copyWithZone:)])
        return [self copy];
    else
        return self;
}
@end

@implementation NSDictionary (MutableDeepCopy)
- mutableDeepCopy
{
    NSMutableDictionary *newDictionary = [[NSMutableDictionary alloc] init];
    NSEnumerator *enumerator = [self keyEnumerator];
    id key;
    while((key = [enumerator nextObject]))
    {
        id obj = [[self objectForKey:key] mutableDeepCopy];
        [newDictionary setObject:obj forKey:key];
    }
    return newDictionary;
}
@end

@implementation NSArray (MutableDeepCopy)
- mutableDeepCopy
{
    NSMutableArray *newArray = [[NSMutableArray alloc] init];
    NSEnumerator *enumerator = [self objectEnumerator];
    id obj;
    while((obj = [enumerator nextObject]))
    {
        obj = [obj mutableDeepCopy];
        [newArray addObject:obj];
    }
    return newArray;
}
@end

@implementation NSSet (MutableDeepCopy)
- mutableDeepCopy
{
    NSMutableSet *newSet = [[NSMutableSet alloc] init];
    NSEnumerator *enumerator = [self objectEnumerator];
    id obj;
    while((obj = [enumerator nextObject]))
    {
        obj = [obj mutableDeepCopy];
        [newSet addObject:obj];
    }
    return newSet;
}
@end

George Lee

A full stack engineer who has coded on front end, back end, and mobile and embedded system. Have worked on large scale systems at eBay and agile environments at startups. Specialties: Python, PHP, Objective C, Java, Perl, SQL, Javascript, IOS, Android, Amazon EC2, Linux and Windows.

Leave a Reply

Your email address will not be published. Required fields are marked *