第一部分 概念篇
http://justsee.iteye.com/blog/1844819
先了解下概念:
浅 复 制:在复制操作时,对于被复制的对象的每一层复制都是指针复制。
深 复 制:在复制操作时,对于被复制的对象至少有一层复制是对象复制。
完全复制:在复制操作时,对于被复制的对象的每一层复制都是对象复制。
注:
1、在复制操作时,对于对象有n层是对象复制,我们可称作n级深复制,此处n应大于等于1。
2、对于完全复制如何实现(目前通用的办法是:迭代法和归档),这里后续是否添加视情况而定,暂时不做讲解。
3、指针复制俗称指针拷贝,对象复制也俗称内容拷贝。
4、一般来讲:
浅层复制:复制引用对象的指针。
深层复制:复制引用对象内容。
这种定义在多层复制的时候,就显得模糊。所以本文定义与它并不矛盾。反而是对它的进一步理解和说明。
5、
retain:始终是浅复制。引用计数每次加一。返回对象是否可变与被复制的对象保持一致。
copy:对于可变对象为深复制,引用计数不改变;对于不可变对象是浅复制,
引用计数每次加一。始终返回一个不可变对象。
mutableCopy:始终是深复制,引用计数不改变。始终返回一个可变对象。
例子:
NSMutableString *mstr = [NSMutableString stringWithString:@"origin"];
NSString *strCopy = [mstr copy];
NSMutableString *mstrCopy = [mstr copy];
NSMutableString *mstrMCopy = [mstr mutableCopy];
// [mstrCopy appendString:@"1111"]; // error
[mstr appendString:@"222"];
[mstrMCopy appendString:@"333"];
// 以上四个对象所分配的内存都是不一样的。而且对于mstrCopy,它所指向的其实是一个imutable对象,是不可改变的,所以会出错,这点要注意,好好理解。
6、
不可变对象:值发生改变,其内存首地址随之改变。
可变对象:无论值是否改变,其内存首地址都不随之改变。
引用计数:为了让使用者清楚的知道,该对象有多少个拥有者(即有多少个指针指向同一内存地址)。
最近有一个好朋友问我,什么时候用到深浅复制呢?那么我就把我所总结的一些分享给大家,希望能帮助你们更好的理解深浅复制喔!
那么先让我们来看一看下边数组类型的转换
1、不可变对象→可变对象的转换:
NSArray *array1= [NSArray arrayWithObjects:@"a",@"b",@"c",@"d",nil];
NSMutableArray *str2=[array1 mutableCopy];
2、可变对象→不可变对象的转换:
NSMutableArray *array2 = [NSMutableArray arrayWithObjects:@"aa",@"bb",@"cc",@"dd",nil];
NSArray *array1=[ array2 Copy];
3、可变对象→可变对象的转换(不同指针变量指向不同的内存地址):
NSMutableArray *array1= [NSMutableArray arrayWithObjects:@"a",@"b",@"c",@"d",nil];
NSMutableArray *str2=[array1 mutableCopy];
通过上边的两个例子,我们可轻松的将一个对象在可变和不可变之间转换,并且这里不用考虑内存使用原则(即引用计数的问题)。没错,这就是深拷贝的魅力了。
4、同类型对象之间的导向保持(不同指针变量指向同一块内存地址):
a、
NSMutableString *str1=[NSMutableString stringWithString:@"two day"];
NSMutableString *str2=[str1 retain];
[str1 release];
b、
NSArray *array1= [NSArray arrayWithObjects:@"a",@"b",@"c",@"d",nil];
NSArray *str2=[array1 Copy];
[array1 release];
通俗的讲,就是甲在执行交通导航任务,但接到上级新的命令要执行新的任务,那么在甲执行新任务之前,需要有人替代甲继续执行交通导航任务。这时候就要用到浅拷贝了。
则简化为:
问:什么时候用到深浅拷贝?
答:深拷贝是在要将一个对象从可变(不可变)转为不可变(可变)或者将一个对象内容克隆一份时用到;
浅拷贝是在要复制一个对象的指针时用到。
第二部分 实际例子
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