Deep copying an NSArray
Is there any built-in function that allows me to deep copy an NSMutableArray
?
I looked around, some people say [aMutableArray copyWithZone:nil]
works as deep copy. But I tried and it seems to be a shallow copy.
Right now I am manually doing the copy with a for
loop:
//deep copy a 9*9 mutable array to a passed-in reference array
-deepMuCopy : (NSMutableArray*) array
toNewArray : (NSMutableArray*) arrayNew {
[arrayNew removeAllObjects];//ensure it's clean
for (int y = 0; y<9; y++) {
[arrayNew addObject:[NSMutableArray new]];
for (int x = 0; x<9; x++) {
[[arrayNew objectAtIndex:y] addObject:[NSMutableArray new]];
NSMutableArray *aDomain = [[array objectAtIndex:y] objectAtIndex:x];
for (int i = 0; i<[aDomain count]; i++) {
//copy object by object
NSNumber* n = [NSNumber numberWithInt:[[aDomain objectAtIndex:i] intValue]];
[[[arrayNew objectAtIndex:y] objectAtIndex:x] addObject:n];
}
}
}
}
but I'd like a cleaner, more succinct solution.
As the Apple documentation about deep copies explicitly states:
If you only need a one-level-deep copy:
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:oldArray copyItems:YES];
The above code creates a new array whose members are shallow copies of the members of the old array.
Note that if you need to deeply copy an entire nested data structure — what the linked Apple docs call a true deep copy — then this approach will not suffice. Please see the other answers here for that.
The only way I know to easily do this is to archive and then immediately unarchive your array. It feels like a bit of a hack, but is actually explicitly suggested in the Apple Documentation on copying collections, which states:
If you need a true deep copy, such as when you have an array of arrays, you can archive and then unarchive the collection, provided the contents all conform to the NSCoding protocol. An example of this technique is shown in Listing 3.
Listing 3 A true deep copy
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData: [NSKeyedArchiver archivedDataWithRootObject:oldArray]];
The catch is that your object must support the NSCoding interface, since this will be used to store/load the data.
Swift 2 Version:
let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData(
NSKeyedArchiver.archivedDataWithRootObject(oldArray))
Copy by default gives a shallow copy
That is because calling copy
is the same as copyWithZone:NULL
also known as copying with the default zone. The copy
call does not result in a deep copy. In most cases it would give you a shallow copy, but in any case it depends on the class. For a thorough discussion I recommend the Collections Programming Topics on the Apple Developer site.
initWithArray:CopyItems: gives a one-level deep copy
NSArray *deepCopyArray = [[NSArray alloc] initWithArray:someArray copyItems:YES];
NSCoding
is the Apple recommended way to provide a deep copy
For a true deep copy (Array of Arrays) you will need NSCoding
and archive/unarchive the object:
NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];