cocoa Shallow Copy与Deep Copy (转)
本文转自 http://www.cnblogs.com/cokecoffe/archive/2012/07/25/2607477.html
先做个关于Core Foundation对象复制简单的介绍:
一般来讲,标准的复制,指的是简单的赋值操作的调用,也就是使用 = 操作符来赋值一个变量给另一个变量,比如说:
1 int a = 5; 2 int b; 3 4 b = a;
那么b就获得了一份a的拷贝,b和a的内存地址是不同的,他们各占不同的内存区域。但是如果你这种方式企图复制一个Core Foundation对象,那么复制的仅仅是对象的引用,而对象本身并没有得到实际的复制。
用代码来说明一切吧:
首先是不可变对象的copy与mutableCopy:
1 //不可变对象的copy 2 NSString *str = [NSString stringWithFormat:@"123"]; 3 4 NSString *cpstr = [str copy];//浅拷贝,str:2 ,cpstr:2 5 6 NSLog(@"STR:%p recount = %ld",str,[str retainCount]); 7 NSLog(@"CPstr:%p recount = %ld",cpstr,[cpstr retainCount]);
2012-07-25 00:19:25.193 copy[11248:403] STR:0x1093145a0 recount = 2
2012-07-25 00:19:25.195 copy[11248:403] CPstr:0x1093145a0 recount = 2
注:str与cpstr指向同一个对象,copy后对象的引用计数增加为2
1 //不可变对象的mutableCopy 2 NSString *str = [NSString stringWithFormat:@"123"]; 3 4 NSMutableString *cpstr = [str mutableCopy];//深拷贝,str:1 ,cpstr:1 5 6 NSLog(@"STR:%p recount = %ld",str,[str retainCount]); 7 NSLog(@"CPstr:%p recount = %ld",cpstr,[cpstr retainCount]);
2012-07-25 00:19:45.694 copy[11278:403] STR:0x7fe55ac145a0 recount = 1
2012-07-25 00:19:45.696 copy[11278:403] CPstr:0x7fe55ac14ad0 recount = 1
注:str与cpstr指向不同的对象,mutableCopy没有影响str的引用计数。
然后是可变对象的copy与mutablecopy
1 //可变对象的copy 2 NSMutableString *str = [NSMutableString stringWithFormat:@"123"];
3
4 NSString *cpstr = [str copy];//深拷贝,str:1 ,cpstr:1 5 6 NSLog(@"STR:%p recount = %ld",str,[str retainCount]); 7 NSLog(@"CPstr:%p recount = %ld",cpstr,[cpstr retainCount]);
2012-07-25 00:20:39.851 copy[11329:403] STR:0x7fe820c14a30 recount = 1
2012-07-25 00:20:39.853 copy[11329:403] CPstr:0x7fe820c14890 recount = 1
注:str与cpstr指向不同的对象,copy没有影响str引用计数。并且copy得到的对象是不可变的,所以不能改变cpstr。
1 //可变对象的mutableCopy 2 NSMutableString *str = [NSMutableString stringWithFormat:@"123"]; 3 4 NSMutableString *cpstr = [str mutableCopy];//深拷贝,str:1 ,cpstr:1 5 6 NSLog(@"STR:%p recount = %ld",str,[str retainCount]); 7 NSLog(@"CPstr:%p recount = %ld",cpstr,[cpstr retainCount]);
2012-07-25 00:21:08.570 copy[11362:403] STR:0x101214a30 recount = 1
2012-07-25 00:21:08.572 copy[11362:403] CPstr:0x101214b20 recount = 1
注意:str与cpstr指向不同的对象。
总结一下就是:
1.不可变对象的copy是浅拷贝,就如retain性质一样,而mutableCopy则是深拷贝,新的内存拷贝。
2.可变对象的copy、mutableCopy都是深拷贝,内存的复制。需注意的是copy得到的对象是不可变的。
关于系统容器类的copy、mutableCopy:与上述一致。
比如:
NSArray *array = [NSArray arrayWithObjects:@"a",@"b",@"c",nil]; NSArray *cparr = [array copy]; NSLog(@"array:%p recount = %ld",array,[array retainCount]); NSLog(@"cpArray:%p recount = %ld",cparr,[cparr retainCount]);
array:0x10c3147a0 recount = 2
cpArray:0x10c3147a0 recount = 2
cparr和cparr指向相同的对象(NSArray)。对象中的元素指向相通的对象(@"a",@"b",@"c");也就是说,把array对象想像成普通对象(例如NSString),一个道理。cparry只是另一份引用而已。画图说明再:
而mutable需要说明一下,先看结果
1 NSArray *array = [NSArray arrayWithObjects:@"a",@"b",@"c",nil]; 2 3 NSArray *cparr = [array mutableCopy]; 4 5 NSLog(@"array:%p recount = %ld object0:%p",array,[array retainCount],[array objectAtIndex:0]);
6 NSLog(@"cpArray:%p recount = %ld object1:%p",cparr,[cparr retainCount],[cparr objectAtIndex:0]);
array:0x7fbc234147a0 recount = 1 object0:0x10357e098
cpArray:0x7fbc234168e0 recount = 1 object1:0x10357e098
继续用图:
这是什么意思呢?cparray是array的可变副本,也就是说数组本身得到了深度拷贝,但是其指向的对象还是一份!理由就是上述打印出来的第一个元素的地址。用C来讲就是指针得到了赋值,两份地址几个,但是地址中保存的数据仍旧是1份。其实我感觉这已经是深拷贝了,因为NSArray里存储的本来就是引用(或者叫地址、指针),而这些东西已经得到了真实的复制了。
Array的深拷贝
两种方法:
1 initWithArray:copyItems: 使用 YES作为参数(这种方式得到的效果跟上面图示效果一致)
使用这种方法,NSArray里的每一个对象会收到copyWithZone消息,这些对象必须遵循NSCopying协议,要不就会导致运行时错误。其实这也是一种真正意义的深拷贝,为什么呢?因为copyWithZone产生的也是浅拷贝。这种拷贝只适合一级深度的拷贝。是不是晕乎了??各位
2.更真正意义的深拷贝!:NSCoding协议
1 NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData: 2 [NSKeyedArchiver archivedDataWithRootObject: oldArray]];
实验的代码:https://github.com/cokecoffe/ios-demo/tree/master/copy
上述是本人对与copy的理解,如有不对的地方,欢迎大家指正,我也是新手一枚!