Does mutableCopy make contained objects mutable?

2019-05-24 13:19发布

问题:

myArray is an NSArray of NSDictionary objects. Will [myArray mutableCopy] contain the original NSDictionary objects or NSMutableDictionary copies? Is there a simple way to make an immutable object graph completely mutable?

回答1:

If you don't mind the amount of time required for a large object graph and you actually want deep copies of objects, you could serialize your object graph and then deserialize it. The easiest way to do this (assuming all your objects are Foundation Collection Objects) is to use the NSPropertyListSerialization class. Serialize your root object to data, then deserialize to your mutable root-level array using the NSPropertyListMutableContainersAndLeaves option. Your resulting root-level mutable array will be a deep-copy and all containers will be mutable. It's important to remember that this really will be a deep-copy, so if you change something in another container, that change won't be reflected in the original objects.

Here is a quick code example:

// Assumes the root-level object is an array, adjust as necessary
- (NSMutableArray*)deepMutableCopyOfArray:(NSArray*)array error:(NSError**)outError
{
  NSError* error = nil;
  NSData* serializedData = [NSPropertyListSerialization dataWithPropertyList:array format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error];
  if( !serializedData ) {
    if( outError ) *outError = error;
    return nil;
  }

  NSMutableArray* mutableCopy = [[NSPropertyListSerialization propertyListWithData:serializedData options:NSPropertyListMutableContainersAndLeaves format:NULL error:&error] retain];
  if( !mutableCopy ) {
    if( outError ) *outError = error;
    return nil;
  }

  return mutableCopy;
}


回答2:

Copies in Cocoa are generally shallow. This means that it only affects the top most object, in this case the array. You'll end up with a mutable array of immutable dictionaries. There is no one liner to make the entire thing mutable like you're asking.



回答3:

The only way to do this is to iterate through the original array, create mutable copies of each object and replace the immutable objects in the mutable array with their mutable brethren. Whew, the word mutable has lost all meaning.

NSArray *mutableArray = [originalArray mutableCopy];

for (NSDictionary *dictionary in originalArray)
{
    NSInteger index = [originalArray indexOfObject:dictionary];
    NSMutableDictionary *mutableDictionary = [dictionary mutableCopy];
    [mutableArray replaceObjectAtIndex:index withObject:mutableDictionary];
}

It should be clear that you can work down even further into the graph with nested for loops. Depending on the size of the array, you may require an autorelease pool to keep memory in check.