Copying a dictionary changes NSMutableArray into NSArray? - objective-c

So I have this NSMutableDictionary object:
pdata=[[NSMutableDictionary alloc] initWithObjectsAndKeys:
#"",#"pid",
#"",#"pname",
[[NSMutableArray alloc] initWithCapacity:1],#"ilist",
nil];
And then I copy this object into another object like this:
NSMutableDictionary *pdataCopy=[[NSMutableDictionary alloc] initWithDictionary:pdata copyItems:TRUE];
But once Ive done this, pdataCopy.ilist is now an NSArray instead of NSMutableArray.
How can I copy a dictionary object whilst maintaining the mutability of the properies inside it?

Actually you can't. You can get a mutable array by
NSMutableArray *mutableArray = [pdataCopy.ilist mutableCopy]

You have three options:
Don't specify copyItems:YES
Scan through the dictionary after copying and replace NSArrays with NSMutableArrays (using mutableCopy) as desired.
Create your own subclass of NSMutableArray that responds to copyWithZone by producing a mutable copy of itself (and use objects of that class in your dictionary).

Related

Is a NSMutableDictionary in a NSDictionary still mutable?

The question is as simple as the title:
Is a NSMutableDictionary in a NSDictionary still mutable? Is the mdict mutable below?
NSMutableDictionary *mdict = [[NSMutableDictionary alloc] init];
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:mdict, #"key", nil];
And, is a NSDictionary in a NSMutableDictionary still immutable?
Further, what if it's array/set instead of dictionary?
Absolutely! Mutability of an object does not change when you place it into a container.
When you place a mutable dictionary into another collection, mutable or immutable, that collection adds a reference to the mutable dictionary object, but it does not change it in any other way. Same goes for placing immutable objects into collections: collections reference these objects without changing their nature.
This remains true while your object is in memory. If you serialize it and then deserialize it back, the process of deserialization may remove mutability. For example, if you save NSMutableDictionary into NSUserDefaults and then read it back, you would get back an immutable dictionary.
Yes. Objects generally don't know when they're placed into a collection, so they can't change their behavior based on that. NSDictionary does copy its keys (precisely so you can change the original object without affecting the dictionary), but it just stores a normal reference to the value.
As long as you access your variables like so
NSMutableDictionary * tempDict = [mdict objectForKey: #"Key"];
NSMutableDictionary * tempDict2 = [arrayVar objectAtIndex: index];
The temp variables retain all the functionality as before

NSDictionary of uninitialized objects?

How can I store an uninitialized object in an NSDictionary?
I think I would do it like this, but I’m not certain that it’s a good approach:
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
[MyObject1 alloc], #"myObject1",
[MyObject2 alloc], #"myObject2"
, nil];
MyObject1 *object = [dict objectForKey:#"myObject1"];
[object init];
Any help would be appreciated.
What you need is to store mutable objects inside the dictionary. Doing this you will be able to modify them after the insertion, because an immutable dictionary doesn't allow to insert a new object.
If for "uninitialized" you mean that the object has only been created with alloc, without init, that's deprecable because init may return a different object from the one returned with alloc. So just store them like you're doing it, and when you need to modify them call the accessors:
NSDictionary *dict = #{ #"myObject1" : [MyObject1 new] , #"myObject2" : [MyObject2 new] };
dict[#"myObject1"].someProperty= someValue;
If your MyObject1 class is immutable, then you have to use a mutable dictionary.

mutable copy copies by reference, not value?

Apparently mutableCopy copies by reference, not value. Ie if I do this:
NSMutableArray arrayA = [arrayB mutableCopy];
then change values of arrayB, then arrayA's values will also be changed.
I think Java has a clone() method to copy by value.. is there an equivalent in objective c?
The mutableCopy method performs “shallow” copy. Each element of arrayA is a reference to an object that is also in arrayB. If you add elements to arrayA (or remove elements), arrayB will be unchanged, and vice versa. But since the elements of arrayA and arrayB reference the same objects, a change to one of those objects “shows up” in both arrays.
If you want a one-level deep copy of arrayB, you can do this:
NSMutableArray *arrayA = [[NSMutableArray alloc] initWithArray:arrayB copyItems:YES];
That will have this effect:
NSMutableArray *arrayA = [[NSMutableArray alloc] init];
for (id element in arrayB) {
[arrayA addObject:[element copy]]; //copies immutable objects to new array
}
To deep copy an array you need to use:
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray: oldArray copyItems:YES];
This performs a copyWithZone: on each object in the array
The regular [NSMutableArray copy] method will, as per Apple's documentation, return a "functionally independent object with values identical to the original at the time the copy was made." You should probably just use that.
To be totally sure that it is mutable, use [[NSMutableArray alloc] initWithArray:[otherArray copy]].

How to copy NSArray to another NSArray?

I have many different NSArrays, and according to the users choice I want one of them to be copied to a new NSArray. How do I copy one NSArray to another?
There can be several ways for this-
array1 = [array2 copy];
Use initWithArray method.
You can also use initWithArray:copyItems: method. (This if for NSMutableArray)
you can use the
NSArray *_newArray = [NSArray arrayWithArray:_oldArray];
or if you prefer better, you can use:
NSArray *_newArray = [[NSArray alloc] initWithArray:_oldArray];
(in that case the object of the first array won't be copied, that get only a retain front he second NSArray, you can remove the object from any array it won't affect the other array, but if you change any object in any NSArray it will be changed in the other one as well because there is both of the old and the new array is working with the same instance of the objects.)
if your plan is to make another instance of the old objects in the new array:
NSArray *_newArray = [[NSArray alloc] initWithArray:_oldArray copyItems:true];
if you are using the ARC, you won't need to do anything else, if you are not, in the case of both -initWithArray: or -initWithArray:copyItems: you should use the [_newArray release]; to release the array after you don't want to use anymore.
As well as
NSArray *newArray = [oldArray copy];
if you need to add/remove from the new array, the simplest way to make a mutable copy is:
NSMutableArray *editableArray = [oldArray mutableCopy];
The above functions both make shallow copies, for deep copy it's as #holex and #rishi mentioned
NSArray *newArray = [[NSArray alloc] initWithArray:oldArray copyItems:true];
NSMutableArray *editableArray = [[NSMutableArray alloc] initWithArray:oldArray copyItems:true];

Is this a correct declaration of an Array in Objective c?

NSArray *arr=[[[NSArray alloc]autorelease]autorelease];
Close, but no cigar.
If you want an autoreleased NSArray, you'd need to use:
NSArray *arr = [[NSArray alloc] init] autorelease];
That said, this will simply get you an empty immutable array, so you'll most likely want to populate it via one of the initWithObjects: style methods. (See the full NSArray class reference for more information.)
Or you can just declare it like this:
[NSArray array];
This gives you an autoreleased instance of the array.