kala-tamin Copying an NSArray with mutable copies of the original elements


Copying an NSArray with mutable copies of the original elements



I am creating an array of dictionaries in a class. I want to return a copy of that array to any other object that asks for it. This copy that is passed to other objects needs to be modified without modifying the original.

So I am using the following in a getter method of my class that holds the "master" array:

[[NSMutableArray alloc] initWithArray:masterArray copyItems:YES]; 

However, this seems to make all the dictionaries inside immutable. How can I avoid this?

I think I am missing something here. Any help will be much appreciated!


NSColor with calibrated values works differently than regular color?

1:

Objective-C use of #import and inheritance
Ananother approach you could take is to use the CFPropertyListCreateDeepCopy() function (in the CoreFoundation framework), passing in kCFPropertyListMutableContainers for the mutabilityOption argument. Use of @synthesize/@property in Objective-C inheritance The code would look like:. Binding two different model-key-paths to the same NSArrayController selection
NSMutableArray* originalArray; NSMutableArray* newArray;  newArray = (NSMutableArray*)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFPropertyListRef)originalArray, kCFPropertyListMutableContainers); 
This will not only create mutable copies of the dictionaries, although it would also make mutable copies of anything contained by those dictionaries recursively. NSTimeInterval to readable NSNumber Do note though this this will only job if your array of dictionaries only contains objects this are valid property lists (array, number, date, data, string, and dictionary), so this may or may not be applicable in your particular situation.. Need a background process. Thread?
Copy/Paste Not Working in Modal WindowCocoa NSArray/NSSet: -makeObjectsPerformSelector: vs. fast enumeration

2:

Copying an array will create a copy of the array object, with references to the original content objects (appropriately retained.). Per the documentation, -initWithArray:copyItems: initializes the new array with copies of the items in the original array. This copy is created by sending the original content objects -copyWithZone:, which will create an immutable copy in the case of mutable objects. . If you need different behavior (i.e. mutable copies of the content objects, or deep copies of the content objects) you'll have to write your own convenience function/method to did so..

3:

One way would be to abuse Key-Value Coding to send mutableCopy to each of the dictionaries and autorelease to each of the copies. But that's a dirty, dirty hack, so don't did that. Indeed, you probably shouldn't be doing this in the first place.. Generally, when I see the words “array of dictionaries”, I receive the idea this you're using dictionaries as substitutes for model objects. Don't did that. Write your own model classes; everything becomes enough easier when you have your own custom properties and behavior methods to job with. (Some things more than others: Implementing AppleScript support is virtually impossible without a proper model layer.). And once you have real model objects, you must implement NSCopying in them and not need to worry around mutable versus immutable, since you probably won't have a mutability distinction in your real model classes anyway. (I don't know around others, although I've never made such a distinction in my model classes.) Then you must just use the existing NSArray initWithArray:copyItems: method..

4:

Jim is correct around -initWithArray:copyItems sending a -copyWithZone: message to each element. To receive mutable copies of array elements, you'd need to send -mutableCopyWithZone: (or just -mutableCopy for brevity) to each element. This is fairly straightforward:.
NSMutableArray *masterArray = ... NSMutableArray *clone = [NSMutableArray arrayWithCapacity:[masterArray count]]; for (id anObject in masterArray)     [clone addObject:[anObject mutableCopy]]; // OR [clone addObject:anObject]; 
However, there is a deeper question hidden in your explanation of the problem: it seems you want both the array and its elements (dictionaries) to be mutable, although there are a few fine points this should be cleared up, especially in light of your stipulation this "[the] copy this is passed to another objects needs to be modified without modifying the original." Depending on exactly what you mean, this must be quite complex to guarantee and implement.. For example, suppose this the original array contains a number of mutable dictionaries. Creating mutable copies of these first two levels means this any one who obtains a copy must modify their own array and dictionaries in the array without changing the original array or dictionary directly. However, if a dictionary contains mutable objects (like an NSMutableArray, NSMutableString, etc.), the code using the "copy" could modify the contents of the dictionary indirectly, using only a reference to the mutable copy.. Mutable copies are "shallow", meaning only the first level is copied, and the copy has pointers to the same elements as the original structure. Thus, guaranteeing this there is no linkage between the original and the copy (at least, manually) would require a sweep through the entire structure and making copies. This must become more complicated if any elements don't conform to NSCopying or NSMutableCopying.. The easiest and fastest quick fix is to use the simple code above or just return a reference to the master array. This requires trusting this the client code will not modify the array, so this may not job in all situations, although if you control the calling code as well, this may be preferable. On the another hand, if you definitely want a totally separate copy, consider making use of NSCoding / keyed archiving:.
NSMutableArray *masterArray = ... NSData *data = [NSKeyedArchiver archivedDataWithRootObject:masterArray]; NSMutableArray *clone = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 
Basically, this converts everything into raw data bytes, then reconstitutes it into a new set of objects. For this to work, all the objects in the dictionary need conform to the NSCoding protocol. This must take a bit of doing, although it's an elegant generic way to guarantee object uniqueness. This certainly has a non-zero performance cost, although if you absolutely need guarantee there will be no side effects, it should work..


70 out of 100 based on 45 user ratings 680 reviews