mutable copy copies by reference, not value? - objective-c

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]].

Related

Objective-C: changing values of one NSMutableArray automatically change values of another NSMutableArray that is assigned to it

I am initializing two NSMutableArrays namely:
NSMutableArray *firstArray = [[NSMutableArray alloc] init];
NSMutableArray *secondArray = [[NSMutableArray alloc] init];
firstArray has some values in it and secondArray has some values in it too.
Then I assigned firstArray to secondArray:
firstArray = secondArray;
Now if I make some changes in firstArray it will also affect secondArray. For example, if I replace or remove a value at certain index from firstArray then that value will also get removed from the secondArray.
So is there any way that changing in firstArray does not affect secondArray?
When you set firstArray = secondArray; you're actually setting their pointers to the same value, rather than setting the items in the first array to be the same as the items in the second array. This means that you have made firstArray and secondArray the exact same object, rather than having the exact same values. If you just want to have a copy of the second array into the first, you just need to specify that.
firstArray = [secondArray mutableCopy];
firstArray = [secondArray copy]; // non-mutable copy
The thing is that NSArray and NSMutableArray in Objective-C is reference type. So, by doing firstArray = secondArray; you actually saying that firstArray and secondArray are the same object. So, no matter how you will access it, thought firstArray pointer or secondArray pointer, you will be accessing exact the same object, so as modifying.
I think what you trying to achieve is to copy your values from fristArray into secondArray, it is possible to do by calling copy or mutableCopy
firstArray = [secondArray copy];
The problem you are facing is that both array is of reference type not value, so both point to same address, so that if you change the value of one it will effects another.
Initialize first array using initWithArray:copyItems: initializtion.
NSArray *firstArray = [[NSArray alloc] initWithArray:secondArray copyItems:YES];
Try initialising both arrays like this
NSMutableArray *firstArray = [[NSMutableArray alloc] init];
NSMutableArray *secondArray = [[NSMutableArray alloc] initWithArray:firstArray];
Hope this helps.

initWithArray vs initWithArray copyItems [duplicate]

This question already has answers here:
Deep copying an NSArray
(6 answers)
Closed 9 years ago.
I have a question what's the difference between these two methods on initializing an array?
I am assuming copyItems will provide a deep copy?
When would you use one versus the other?
Thank you!
The documentation for these methods is the first result (for me) with a little Google search.
initWithArray: copyItems:
has this documentation:
Initializes a newly allocated array using anArray as the source of data objects for the array.
Parameters
array
An array containing the objects with which to initialize the new array.
flag
If YES, each object in array receives a copyWithZone: message to create a copy of the object—objects must conform to the NSCopying
protocol. In a managed memory environment, this is instead of the
retain message the object would otherwise receive. The object copy is
then added to the returned array. If NO, then in a managed memory
environment each object in array simply receives a retain message when
it is added to the returned array.
whereas initWithArray: has this documentation:
Initializes a newly allocated array by placing in it the objects contained in a given array.
e.g.
Note: Since NSArray isn't mutable, my corresponding implementations aren't directly usable
`array2 = [[NSArray alloc] initWithArray:array1 copyItems:YES]`
//would correspond to:
array2 = #[
[array1[0] copy],
[array1[1] copy],
[array1[2] copy],
...
[array1[n] copy],
]
whereas
array2 = [[NSArray alloc] initWithArray:array1]
//would correspond to:
array2 = #[
array1[0],
array1[1],
array1[2],
...
array1[n],
]
//or
array2[0] = array1[0];
array2[1] = array1[1];
array2[2] = array1[2];
...
array2[n] = array1[n];
initWithArray: initializes a new array and places in it all the objects contained in a given array. This means that each object in the given array will receive a retain. Hence, if you edit an object in the new array, you will modify that object even in the given array. (see shallow copy)
On the other hand, initWithArray:copyItems:, if YES is passed as second argument, will provide a deep copy.
Beware that if you need to deeply copy an entire nested data structure then this approach will not suffice. (see the Apple documentation)
Example:
NSMutableString *s = [[NSMutableString alloc] initWithString:#"hello"];
NSArray *a = #[s];
NSArray *b = [[NSArray alloc] initWithArray:a];
[a[0] appendString:#" there"];
after these lines the arrays a and b will contain the mutable string "hello there"
NSMutableString *s = [[NSMutableString alloc] initWithString:#"hello"];
NSArray *a = #[s];
NSArray *c = [[NSArray alloc] initWithArray:a copyItems:YES];
[a[0] appendString:#" there"];
while after these lines the array c will contain the mutable string "hello" and the array a will contain the mutable string "hello there"

what's property copy means in Cocoa's Framework?(like UITabBar's items property)

In UITabBar.h, a propery signed copy
#property(nonatomic,copy) NSArray *items; // get/set visible
It's a array
And what "copy" means?
copy NSArray container obj?
copy every obj NSArray contains?
or something.
so there's a test
UITabBar* testBar = [[UITabBar alloc] init];
UITabBarItem* item = [[UITabBarItem alloc] init];
NSArray* array = [[NSArray alloc] initWithObjects:item, nil];
NSLog(#"bar:%p,%d", testBar, testBar.retainCount);
NSLog(#"item:%p,%d", item, item.retainCount);
NSLog(#"array:%p,%d", array, array.retainCount);
testBar.items = array;
NSLog(#"that item:%p,%d", [testBar.items lastObject], [[testBar.items lastObject] retainCount]);
NSLog(#"testBar.items:%p,%d", testBar.items, testBar.items.retainCount);
result
bar:0x96a9750,1
item:0x96aa230,2
array:0x96aa280,1
that item:0x96aa230,2
testBar.items:0x96aa280,6
why neither container array nor obj in array has been "copied"?
Two things:
collection -copy is always shallow. It doesn't copy the collections elements (In fact, nothing guarantees that these elements are even copyable – i.e. are conforming to NSCopying protocol). This explains why obj is not copied – it doesn't get any extra retain.
Foundation tries to optimizes its implementation of -copy to -retain whenever is possible. For example, -[NSString copy] is a retain for immutable strings. Since collection copies are shallow, the same optimization works for immutable collections. That's why array is not copied but just retained.
The reason the copy has not been made in this case is that NSArray is immutable. You do not need to make a copy of it to guard against changes to the array, because such changes cannot be made; it is sufficient to retain the same immutable array.
If you try this experiment with NSMutableArray, you will get a different result.

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];

Terminology question regarding looping thru an NSArray in Objective-C

When you have an NSArray and you want to evaluate and change the elements, you can't change the array from inside the loop. So, you create a mutable copy that can be changed.
code example:
NSMutableArray *bin = [NSMutableArray arrayWithObjects:#"0", #"1", #"2", #"3", #"4", #"5", #"6", #"7", nil];
NSMutableArray *list = [NSMutableArray arrayWithObjects:#"a1", #"b2", #"c3", #"e4", nil];
NSMutableArray *listHolder = list; // can't mutate 'list' within loop so create a holder
for (int i = 0; i < [list count]; i++) {
[listHolder replaceObjectAtIndex:i withObject:[bin objectAtIndex:i]];
}
What is that second array listHolder called? I mean, what term is used to refer to an array in this context.
This is perfectly valid:
NSMutableArray *bin = [NSMutableArray arrayWithObjects:#"0", #"1", …, #"7", nil];
NSMutableArray *list = [NSMutableArray arrayWithObjects:#"a1", …, #"e4", nil];
// NSInteger should be used instead of int
for (NSInteger i = 0; i < [list count]; i++) {
[list replaceObjectAtIndex:i withObject:[bin objectAtIndex:i]];
}
You're not allowed to change the array inside a for … in or NSEnumerate loop, but using an index is perfectly valid.
What troubles me is your misunderstanding of pointers.
If it were a loop in which you weren't allowed to mutate the array this wouldn't copy the array but only the pointer to the array, effectively modifying the array you're not allowed to. (I'm not even sure if this works.)
Instead of just copying the pointer
// can't mutate 'list' within loop so create a holder
NSMutableArray *listHolder = list;
make a true copy:
NSMutableArray *copy = [[list mutableCopy] autorelease];
In case I really have to make a copy I try to name it according to its content. For example:
NSMutableArray *views;
NSMutableArray *reorderedViews = [views mutableCopy];
// reorder reorderedViews
Sometimes it's hard to find a good enough name, then I usually just use nameCopy.
In this context listHolder would be called a copy.
Your code has a bug though. This line is not actually making a copy, it is only letting listHolder and list both reference the same array object:
NSMutableArray *listHolder = list;
This would be an actual copy:
NSMutableArray *listHolder = [list mutableCopy];
Make sure that you use mutableCopy and not just copy if you want the copy to be mutable. The copy method will return immutable variants on all mutable classes such as NSMutableSet, NSMutableDictionary, and so forth.
Also as others have noted it is only inside the for (item in collection) loop that the enumerated collection can not be mutated. In a normal for (;;) mutation is perfectly ok, but can lead to strange result if the number of items in the collection changes.
There is not specific stylistic or common name for this that is universally used, it is your code afterall, and if there appropriate terms for them use them.
Having said that generally if you don't have specific names in this sort of situation then people refer to the original list as the "source" (src) and the final list as "destination" (dst), just like in a memory blitting style operation.
A temporary mutable copy of the original NSArray would be how I would refer to it.