Chapter 11 : 复制对象
1. 回顾继承部分的代码如下:
1 // XYPoint类声明 2 // XYPoint.h文件 3 4 #import <Foundation/Foundation.h> 5 6 @interface XYPoint : NSObject 7 { 8 int x; 9 int y; 10 } 11 12 @property int x; 13 @property int y; 14 15 - (void)setX:(int)xVal andY:(int)yVal; 16 17 @end
1 // XYPoint类定义 2 // XYPoint.m文件 3 4 #import "XYPoint.h" 5 6 @implementation XYPoint 7 8 @synthesize x; 9 @synthesize y; 10 11 - (void)setX:(int)xVal andY:(int)yVal 12 { 13 x = xVal; 14 y = yVal; 15 } 16 17 @end
1 // Rectangle类声明 2 // Rectangle.h文件 3 4 #import <Foundation/Foundation.h> 5 6 @class XYPoint; 7 8 @interface Rectangle : NSObject 9 { 10 int width; 11 int height; 12 XYPoint *origin; 13 } 14 15 @property int width; 16 @property int height; 17 18 - (XYPoint *)origin; 19 - (void)setOrigin:(XYPoint *)pt; 20 - (void)setWidth:(int)w andHeight:(int)h; 21 - (int)area; 22 - (int)perimeter; 23 24 @end
1 // Rectangle类定义 2 // Rectangle.m 3 4 #import "Rectangle.h" 5 6 @implementation Rectangle 7 8 @synthesize width; 9 @synthesize height; 10 11 - (void)setWidth:(int)w andHeight:(int)h 12 { 13 width = w; 14 height = h; 15 } 16 17 - (void)setOrigin:(XYPoint *)pt 18 { 19 origin = pt; 20 } 21 22 - (XYPoint *)origin 23 { 24 return origin; 25 } 26 27 - (int)area 28 { 29 return width * height; 30 } 31 32 - (int)perimemter 33 { 34 return (width + height) * 2; 35 } 36 37 @end
1 // main() 2 3 #import "Rectangle.h" 4 #import "XYPoint.h" 5 6 int main(int argc, const char *argv[]) 7 { 8 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 9 10 Rectangle *myRect = [[Rectangle alloc] init]; 11 [myRect setWidth:5 andHeight:8]; 12 13 XYPoint *myPoint = [[XYPoint alloc] init]; 14 [myPoint setX:100 andY:200]; 15 16 myRect.origin = myPoint; 17 18 NSLog(@"Origin at (%i, %i)", myRect.origin.x, myRect.origin.y); 19 20 // Key Line, Attention!!! 21 [myPoint setX:50 andY:50]; 22 23 NSLog(@"Origin at (%i, %i)", myRect.origin.x, myRect.origin.y); 24 25 [myRect release]; 26 [myPoint release]; 27 28 [pool drain]; 29 30 return 0; 31 }
注意main()方法的Line 16
myRect.origin = myPoint;
这样赋值的结果仅仅是将对象myPoint的地址复制到myRect.origin中,即在赋值操作结束时,myRect.origin和myPoint都指向内存中的同一个地址。
所以,将一个变量赋值给另一个对象仅仅是创建另一个对这个对象的引用。即有:
1 NSMutableArray *dataArray1; 2 // 完成dataArray1对象的数据初始化 3 ... 4 NSMutableArray *dataArray2; 5 dataArray2 = dataArray1; 6 7 [dataArray2 removeObjectAtIndex:0];
这样在Line 7中的removeObjectAtIndex将从这两个变量引用的同一个数组中删除第一个元素。
2. 复制对象示例代码:
1 #import <Foundation/Foundation.h> 2 3 int main(int argc, const char *argv[]) 4 { 5 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 6 7 NSMutableArray *dataArray = [[NSMutableArray arrayWithObjects:@"one", @"two", @"three", @"four", nil]]; 8 NSMutableArray *dataArray2; 9 10 // Key Line, 因为没有复制对象,所以dataArray和dataArray2指向的数据是同一个内存地址 11 dataArray2 = dataArray; 12 [dataArray2 removeObjectAtIndex:0]; 13 NSLog(@"dataArray:"); 14 for (NSString *elem in dataArray) 15 NSLog(@"%@", elem); 16 17 NSLog(@"dataArray2:") 18 for (NSString *elem in dataArray2) 19 NSLog(@"%@", elem); 20 21 // 执行复制对象 22 dataArray2 = [dataArray mutableCopy]; 23 [dataArray2 removeObjectAtIndex:0]; 24 NSLog(@"dataArray"); 25 for (NSString *elem in dataArray) 26 NSLog(@"%@", elem); 27 28 NSLog(@"dataArray2:"); 29 for (NSString *elem in dataArray2) 30 NSLog(@"%@", elem); 31 32 [pool drain]; 33 34 return 0; 35 }
3. copy和mutableCopy : 利用copy和mutableCopy的方法,可以创建对象的副本, 即:
1 dataArray2 = [dataArray mutableCopy];
意义是在内存中创建一个新的dataArray副本,并复制了它的所有元素。随后执行语句:
1 [dataArray2 removeObjectAtIndex:0];
删除了dataArray2中的第一个元素,但不会删除dataArray中的。
PS :
-> 产生一个对象的可变副本并不要求被复制的对象本身是可变的,也可以创建可变对象的不可变副本。
-> 在产生数组的副本时,数组中每个元素的Retain Count将通过复制操作自动增1。所以,需要执行
1 [dataArray2 release];
释放它的内存。
4. 浅复制和深复制:
示例代码:
此处由于使用的浅复制,所以在执行
[mStr appendString:@"ONE"];
后,dataArray与dataArray2的第一个元素都发生了改变。这里可理解为dataArray里存储的元素本来就是一个内存地址(NSMutableString对象),
在复制地过程中虽然生成了dataArray的副本给dataArray2,但是他们存储的元素指向的内存地址依然相同,故字符串的改变都是指向同一个内存地址。
-> 深复制:要解决上面的问题,那就得在创建数组副本时也要创建数组中每个元素对象的副本:
mStr = [NSMutableString stringWithString:[dataArray2 objectAtIndex:0]];
然后,可以更改mStr, 并使用replaceObjectAtIndex:withObject:方法将它添加到数组中:
[mStr appendString:@"ONE"];
[dataArray2 replaceObjectAtIndex:0 withObject:mStr];
这样,改变的数据就不会影响到[dataArray objectAtIndex:0], 因为这个数组元素已经经过的深复制。