专栏名称: Cocoa开发者社区
CocoaChina苹果开发中文社区官方微信,提供教程资源、app推广营销、招聘、外包及培训信息、各类沙龙交流活动以及更多开发者服务。
目录
相关文章推荐
51好读  ›  专栏  ›  Cocoa开发者社区

OC深拷贝PK浅拷贝,欢迎来战!

Cocoa开发者社区  · 公众号  · ios  · 2017-02-23 18:22

正文

本文来自CC论坛noah1985的讨论帖

楼主:noah1985


最近简书上经常和别人就这个问题进行一些PK,其实观点上是一致的,但就这个“深拷贝”名词上说不到一块儿上去,即使我“努力”解析也无法达到共识,这真的有这么难吗?


先说说C的赋值语句。

int a = 10;

int b = a;

这实质上是一次值拷贝过程,改变b的值并不会让a同时改变。大概也很多人没有意识到这也是一种拷贝行为。


然后说说指针

int a = 10;

int *pa = &a;

int *pb = pa;


第二第三行也是拷贝行为,但第二种属于浅拷贝。深浅拷贝在C中是用来描述指针拷贝行为的,除非手动实现否指针的赋值行为是属于浅拷贝。


但OC广义来说就不要用上面那套了,因为很容易让人误解。

例如我和别人争论的中心在于我的观点是,Foundation主要的类型只有浅拷贝,没有深拷贝。例如NSArray,NSMutableArray等。。。

对方反驳的是 不可变对象进行mutableCopy和可变对象进行copy/mutableCopy就是深拷贝。

原因就是上面说的指针问题。

但众所周知的是,对Foundation的不可变对象进行Copy,实际上只是对该对象进行retainCount + 1然后返回该对象的地址,ARC里实际上连retainCount都没给你加。。尤其是对于字符串常量。因为是没有必要的,本身对象不可变,你要一个副本也无法修改,失去了拷贝的意义。下面里有没有copy都是一样的,属于没意义的。。。所以他们只是只是指针复制了。

NSString *s = @"abc";

NSString *t = s.copy;


而争论在这里展开了,对方认为下面这个行为属于深拷贝。我则不认为。原因在于我对拷贝的认知是两者完全一模一样,包括它们的类型,状态。但下面两个类是一样的吗?明显不是,或不完全是。它是先拷贝然后对原来的对象进行了修改,属于两个动作。

NSString *s = @"abc";

NSMutableString *t = s.mutableCopy;


再说回来关于NSArray,对其进行mutableCopy和上面的字符串一样,拷贝出来的不是相同的东西,类型也变了。但明显的它指向的是和原本的对象是一致的这种内容没有被拷贝,只是容器拷贝的行为凭什么被叫做深拷贝?而连容器内对象都进行拷贝的行为,有些人叫做“完全拷贝”。。。也的确够完全的。


实质上。。。争论这个也没有什么实质上的意义。只是争论过程中顺便理清了所谓拷贝的本质而已。


ID:ohMyKing


"ARC里实际上连retainCount都没给你加" 这个结论是怎么得出来的.. 不是常量的话,retainCount都是会加的。

而字符串常量的retainCount固定是NSUIntegerMax.

楼主不认为那个例子属于深拷贝,是由于楼主把深拷贝定义为“两者完全一模一样” ,但其实,很多oc书籍上对于深拷贝的描述 都是 只要指针变了(两个指针指向的内存不一样,说明开辟了新内存),就是深拷贝,而浅拷贝就是单纯的指针赋值,指向的内存还是同一块。

就是说,只要开辟了新内存的拷贝就是深拷贝。

说来说去就是一个概念的定义的问题,,对于“深拷贝”这个概念的定义不一样,得出的结论就不一样。 


ID:ph101


数组和数组数组元素是两种东西。

OC概念中只要申请了新的内存空间就是深拷贝。

和其他语言可能不一样。

你所理解的深拷贝应该可以用完全拷贝来阐述。

OC中深拷贝要求仅仅为单层指针拷贝。 


ID:高军全


小弟认为是这样的, 这种问题不该用常有的逻辑思维来评判, 这是苹果定的, 所以得听apple说

深拷贝和浅拷贝的本质是有没有开辟新的内存空间并复制对象的内容以及它所包含的对象的内容, 我们举几个例子

NSString *str1 = @"123";

NSString *str2 = [str1 copy];

NSString *str3 = [str1 mutableCopy];

通过log我们会知道str3是开辟了内存空间, mutableCopy这个方法在苹果官方的定义是返回mutableCopyWithZone返回的对象, 这复制了内容也开辟了内存,这是深拷贝

NSArray *arr = [NSArray arrayWithObjects:str1, nil];

NSMutableArray *arr2 = [arr mutableCopy];

问题来了, arr2是深拷贝吗? 如果用苹果自己的定义来说, 它不是, 因为arr的firstObject只是复制了指针而已, 但是苹果把它归结为deep copy(深拷贝).

所以, 不必纠结

都提倡****, 但是国外随便说, 我们行吗?

苹果规定不行用私有api, Google Voice就用了, 也可以上线啊, 自己的iBook也是各种私有api啊

但我们编码时还是要照apple说的 , 只有开辟了新的内存空间, 并且对象的和对象所包含的对象的内容都被复制, 才是深拷贝!


ID:scribbler


浅拷贝: 也叫位拷贝,只对指针的拷贝,拷贝后两个指针指向同一个内存空间

深拷贝: 也叫值拷贝,对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。


这是基本定义,浅拷贝会出现什么问题

1、浅拷贝只拷贝了指针,使得两个指针指向同一个地址,对象释放时,会造成同一份资源析构2次,造成程序崩溃。

2、浅拷贝指针指向同一块内存,任何一方的变动都会影响到另一方。


深拷贝采用了在堆内存中申请新的空间来存储数据,可以避免指针悬挂。

但是都用深拷贝会造成资源浪费,

OC 对内存管理使用引用计数的东西。

OC copy 建立一个索引计数为1的对象,然后释放旧对象

NSString *srcStr = @"123";

NSMutableString *copyStr =  [srcStr mutableCopy];

[copyStr appendString:@"abc"];

NSLog(@"srcStr=%p:%@, copyStr=%p:%@", srcStr,srcStr, srcStr,copyStr);

我认为mutableCopy是一种深拷贝,NSMutableString继承NSString,在拷贝里,并不强制要求对象的结构必须完全一致。

在深拷贝赋值运算符函数步骤

步骤1、释放原来的内存空间

步骤2、再重新开辟要赋值的对象的大小的空间

步骤3、再将另一个对象的值拷贝给this对象


ID:wszcug


好了大家不要争了,其实看成一块内存就好了

1:所谓浅拷贝,实际上是这样的:一个指针,指向一块内存1, 对这块内存进行浅拷贝,其实就是提取了 这块内存的地址,然后把他给另外一个 8字节的指针类型存放,这时候浅拷贝的意义就是 多了一个8字节(64位系统)的指针,同样指向这块内存,综合来看,内存并未有任何变化,但是现在有两个指针指向它,

并且这两个指针存放的值一样,就是这块内存的首地址,但是 这两个指针本身的值(8字节)不一样,所以就是:两个不同的指针指向同一块内存

2:所谓深拷贝:实际上是这样: 一个指针,指向了一块内存1,对这块内存1进行深拷贝,首先,我要开辟一块跟这块内存一样大的内存2,然后把内存1里面的值(请注意,这里是值)丝毫不差的拷贝到内存2里面去,然后再搞一个指针,指向内存2,这时候来看,有两块内存了,并且两块内存毫不相干,只是里面的值一样而已。 修改其中一个并不会影响另外一个

3:完全拷贝:深拷贝 请记住是内存里面的值拷贝,如果内存里面放的都是一些基本类型,比如char int long 等等,无所谓, 但如果是指针类型或者对象类型,深拷贝只能保证拷贝到第一层,也就是如果有指针类型,他只拷贝了指针的值。

想象一下:如果内存1里面有个指针,指向了一个对象a, 这时候对内存1进行深拷贝得到了内存2,我们知道深拷贝是值拷贝,这时候内存2里面同样有一个指针指向了对象a ,因为他俩存的指针类型的值是一摸一样的,所以深拷贝不能保证其内部指针指向的对象的拷贝。 这也就是为什么深拷贝的对象必须也要实现nscopy协议 才能实现第二层也能实现深拷贝,否则就是浅拷贝

总结:浅拷贝一定是这块内存首地址指针拷贝,深拷贝一定是这块内存所有值(包括指针的值)的值拷贝。如果你想实现完全拷贝,那么请让你内存中所有指针所指向的对象实现nscopying协议,否则深拷贝只能完全拷贝第一层,拷贝不到第二层

结合下面的图体会一下


ID:duanyutian


有人说浅拷贝就是找了一个老婆,同生共死,深拷贝就是找了个情人。



辣么,开始上票


点击“阅读原文”加入现场PK