Assignment,
|
Modified: |
Assignment
Assignment of an object creates an alias.
Example
The following:
creates a mutable array, dataArray, with references to NSString elements,
assigns dataArray2 the object referenced by dataArray, creating an alias
modifies an element of the original array, dataArray,
prints both arrays, both reflect the modification because both alias the same NSMutableArray object.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *dataArray = [NSMutableArray arrayWithObjects:
[NSMutableString stringWithString: @"one"],
[NSMutableString stringWithString: @"two"],
[NSMutableString stringWithString: @"three"],
nil];
NSMutableArray *dataArray2;
NSMutableString *mStr;
NSLog(@"1. dataArray: %@", dataArray);
dataArray2 = dataArray ; // assignment alias array
mStr = [ dataArray objectAtIndex: 0]; // mStr references @"one"
[ mStr appendString: @"ONE"]; // then change first element
NSLog(@"2. dataArray: %@", dataArray);
NSLog(@"3. dataArray2: %@", dataArray2);
[pool drain];
return 0;
}
|
1. dataArray: ( 2. dataArray: ( 3. dataArray2: ( |
Comparison
== tests that two variables hold the same value.
dataArray2 = dataArray; dataArray2 == dataArray;
is true because both variables hold a reference to the same object.
NSMutableArray *dataArray = [NSMutableArray arrayWithObjects:
[NSMutableString stringWithString: @"one"],
[NSMutableString stringWithString: @"two"],
[NSMutableString stringWithString: @"three"],
nil];
NSMutableArray *dataArray2= [NSMutableArray arrayWithObjects:
[NSMutableString stringWithString: @"one"],
[NSMutableString stringWithString: @"two"],
[NSMutableString stringWithString: @"three"],
nil];dataArray2 == dataArray; // false
is false because each variable holds a reference to a different object.
Even though the values of the two objects are equal, they are not the same object.
Shallow Versus Deep Copying
Foundation classes implement copy and mutableCopy to create a copy of an object.
For your own classes, implement a copy method conforming to the <NSCopying> protocol.
For classes that distinguish between mutable and immutable objects, implement mutableCopy method conforming to the <NSMutableCopying> protocol.
Example
The following:
creates a mutable array, dataArray, with references to NSString elements,
creates a copy of a mutable array, dataArray2, using the copy method for NSMutableArray,
modifies an element of the original array, dataArray
prints both arrays, both reflect the modification because the array elements of both arrays alias the NSString objects.
The copy method of NSMutableArray performs a shallow copy, copying only object references.
Modifying an element in dataArray, because only the object references were copied, side-effects dataArray2.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *dataArray = [NSMutableArray arrayWithObjects:
[NSMutableString stringWithString: @"one"],
[NSMutableString stringWithString: @"two"],
[NSMutableString stringWithString: @"three"],
nil];
NSMutableArray *dataArray2;
NSMutableString *mStr;
NSLog(@"1. dataArray: %@", dataArray);
dataArray2 = [dataArray copy]; // make a shallow copy
mStr = [ dataArray objectAtIndex: 0]; // mStr references @"one"
[ mStr appendString: @"ONE"]; // then change first element
NSLog(@"2. dataArray: %@", dataArray);
NSLog(@"3. dataArray2: %@", dataArray2);
[dataArray2 release];
[pool drain];
return 0;
}
|
1. dataArray: ( 2. dataArray: ( 3. dataArray2: ( |
Example
The main() function is unchanged from Shallow Copy above.
A category, MyNSMutableArray, adds a deep copy method, over-riding the NSMutableArray copy.
Recall that categories do not create a new class but extend an existing class by adding methods only.
The following:
creates a mutable array, dataArray, with references to NSString elements,
creates a copy of a mutable array, dataArray2, using the copy method of MyNSMutableArray, a category of NSMutableArray
modifies an element of the original array, dataArray
prints both arrays, both reflect the modification.
The copy method of NSMutableArray performs a shallow copy, copying only object references.
Modifying an element in dataArray, because only the object references were copied, side-effects dataArray2.
Note that copy is specific for NSString because:
for ( NSString *elem in self)
[array addObject: [[NSString alloc] initWithString: elem]];Alternatively, declaring id type allows any type, assuming the object responds to the copy.
Because the compiler has no way of checking the object types that will be referenced at run-time, it gives a warning; should some object not have a copy, there is a run-time crash.
Because NSString happens to have a deep copy, in this case everything works.
for( id *elem in self)
[array addObject: [elem copy]];
|
#import <Foundation/Foundation.h> @interface NSMutableArray (MyNSMutableArray) - (NSMutableArray *) copy; @end @implementation NSMutableArray (MyNSMutableArray) -(NSMutableArray *) copy { NSMutableArray *array = [[NSMutableArray alloc] init];
return array; |
1. dataArray: ( 2. dataArray: ( 3. dataArray2: ( |
|
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *dataArray = [NSMutableArray arrayWithObjects:
[NSMutableString stringWithString: @"one"],
[NSMutableString stringWithString: @"two"],
[NSMutableString stringWithString: @"three"],
nil];
NSMutableArray *dataArray2;
NSMutableString *mStr;
NSLog(@"1. dataArray: %@", dataArray);
dataArray2 = [dataArray copy]; // make a deep copy
mStr = [ dataArray objectAtIndex: 0]; // mStr references @"one"
[ mStr appendString: @"ONE"]; // then change first element
NSLog(@"2. dataArray: %@", dataArray);
NSLog(@"3. dataArray2: %@", dataArray2);
[pool drain];
return 0;
}
|
Memory leaks
copy messages allocate objects which is released by:
[dataArray2 release]; However, the copy allocates objects and creates several leaks; recall that adding objects to NSMutableArray increments the retain count.
Also, when the copied NSMutableArray dataArray2 object is released, there is no category -dealloc method that is called.
The simplest approach to autorelease each allocation within the copy as done below.
Note that the copy operation does not release the allocated NSMutableArray, that is the responsibility of the caller.
| -(NSMutableArray *) copy { NSMutableArray *array = [[NSMutableArray alloc] init]; for ( NSString *elem in self) [array addObject: [[[NSString alloc] autorelease] initWithString: elem]]; return array; } |
A better solution, releases the memory immediately rather than waiting for autorelease.
| -(NSMutableArray *) copy { NSMutableArray *array = [[NSMutableArray alloc] init]; for ( NSString *elem in self) { [array addObject: [[NSString alloc] initWithString: elem]]; [[array lastObject] release]; } return array; } |