Shallow Copy与Deep Copy


第一部分 概念篇

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

 

参考:apple文档https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Collections/Articles/Copying.html

实验的代码:https://github.com/cokecoffe/ios-demo/tree/master/copy


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值