老生常谈各种拷贝

来源:互联网 发布:js 控制div的隐藏 编辑:程序博客网 时间:2024/05/22 07:07
在最开始,我们需要清楚一些关于内存分配方式的基础知识。
一般来说分为栈、堆、静态变量存储区、全局变量存储区、代码区。
前两个大家都懂的。通常将后三个合并称之为静态存储区,存储的是一些全局变量、静态变量、常量、执行代码等。
在Objective-C中,不可变数组、不可变字典以及一些常量字符串,都是分配在这个区域的,我们先要明确这一点。

所以在提到深浅拷贝的时候,用NSArray举例子的,只能说对内存分配方式就不清楚,因为对一个不可变数组进行copy、mutableCopy操作,它实际上返回的是一个对象,跟深浅拷贝无关,因为都是按照retain来处理的。这也就是很多所谓教程提到的指针拷贝。

下面先说一下可变拷贝和不可变拷贝,分别遵循NSCopying和NSMutableCopying协议,需要对应实现copyWithZone:方法和mutableCopyWithZone:方法。
分两种情况来讲,一种是系统容器类,一种是自定义类。
一、系统容器类。
例如NSArray、NSDictionary,它们已经实现了上面两个协议。
对于它们来说,规则很简单,obj2 = [obj1 copy]返回的必然是一个不可变对象,无论obj1是可变对象还是不可变对象。如果obj1是一个不可变对象,那么它们指向同一个对象,也是上一条我提到过的。
obj2 = [obj1 mutableCopy]返回的必然是一个可变对象,无论obj1是可变对象还是不可变对象。即使obj1也是一个可变对象,它们仍指向不同地址,是两个对象。
二、自定义类。
因为copyWithZone:和mutableCopyWithZone:完全由自己来实现,所以代码的不同实现方式,决定了返回对象是什么。
在demo中Element类的copyWithZone:有注释,感兴趣的可以参考一下。
极端一点的例子,例如你直接在copyWithZone:方法中return self;那么obj2 = [obj1 copy]相当于obj2 = obj1,只是一个assign,没有做任何其它操作。 

下面着重说一下浅拷贝和深拷贝。
首先顾名思义,无论是浅拷贝还是深拷贝,都有一个拷贝在里面,那些说浅拷贝相当于retain、什么所谓指针拷贝的,建议还是不要误人子弟了好吧。
这里我们以NSMutableArray为例
NSMutableArray *element = [NSMutableArray arrayWithObject:@1];
NSMutableArray *array = [NSMutableArray arrayWithObject:element];

id mutableCopyArray = [array mutableCopy];
这一句代码就是浅拷贝,是拷贝了容器自身,返回了一个新的可变数组,指向不同的内存地址。
内部的元素依然是公用的,也就是说,mutableCopyArray[0]也指向element,[mutableCopyArray[0] addObject:***]会影响到array的结果。

id deepMutableCopyArray = [array test_deepMutableCopy];
这一句代码对应的实现是深拷贝,首先它也拷贝了容器自身,返回了一个新的可变数组,指向不同的内存地址。
其次,对内部的元素也进行了拷贝动作,也就是说deepMutableCopyArray[0]是一个新的可变数组,和原来的element是两个数组,修改[deepMutableCopyArray[0] addObject:***]并不会影响到array的结果。

上面这些代码在demo中有,感兴趣或者抱怀疑态度的,可以运行了试试或者加一些log来分析结果。

大多程序猿被洗脑的程度让人难以置信
前面已经给出了结论,系统容器的copy和mutableCopy都是浅拷贝,区别是可变拷贝和不可变拷贝。
但是仍然没什么卵用,仍然有好多人在坚持自己的看法,并且觉得我是在胡扯,我只好来列出证据证明我自己了。

首先,维基百科上的定义链接如下:
https://en.wikipedia.org/wiki/Object_copying
具体参考Shallow Copy和Deep Copy。

其次,stackoverflow上一个比较多人讨论的帖子,链接如下:
http://stackoverflow.com/questions/184710/what-is-the-difference-between-a-deep-copy-and-a-shallow-copy

最后,说mutableCopy是深拷贝的,可以不可以给我解释一下mutable的英文意思,人可以无知,但是不可以把无知当个性。

1、“copy 与 mutableCopy都是返回一个新的对象”
还是分两种情况来说,
一是自定义对象,取决于你的copyWithZone和mutableCopyWithZone方法。
二是系统容器类(NSArray、NSDictionary……)
参考两个原则:首先,copy方法返回的是一个不可变容器,无论原来是可变或者不可变,mutableCopy返回的是一个可变容器,无论原来是可变还是不可变。其次,不可变容器的copy方法,会直接返回自己,什么别的操作都没有,相当于在copyWithZone:方法中直接return self;

2、指针拷贝。
本来指针拷贝这个说法就不严谨,严格上来说是指针的值拷贝,因为还有另一种指针的内容拷贝这里不多做介绍。
而且Objective-C这门语言就没有这么个说法,在Objective-C中有更贴切的说法叫做assign。
为了方便大家对比理解,我们就默认以指针的值拷贝来向下解释,指针的值拷贝在C/C++中的代码体现如下:
int firstvalue = 5, secondvalue = 15;
int *p1 = &firstvalue;
int *p2 = &secondvalue;
p1 = p2;           // p1 = p2 (value of pointer is copied)
指针p2的值被复制了以后赋值给p1。

但是这里所说的指针的值拷贝和前面所提的深浅拷贝,是有所不同的,为了方便理解,举个例子:
指针的值拷贝,类似于我们用excel,A1单元格就是指针p1,填的内容0x****01就是指针p1的值,也就是firstvalue的地址,同理A2单元格就是指针p2,填的内容0x****02就是指针p2的值。p1 = p2这一步操作,就相当于我们ctrl+c复制A2单元格内容,ctrl+v粘贴到A1单元格。
我们前面提到的深浅拷贝,类似于我们在桌面上选择一个文件ctrl+c,然后ctrl+v粘贴会出现一个副本。

总结一下,如果非要使用指针的值拷贝这个说法的话:
1、不可变系统容器的copy方法,可以认为是指针的值拷贝。
2、其他形式的系统容器的copy或者mutableCopy方法,都是浅拷贝。
3、系统容器类,如果想要实现深拷贝,使用- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag;这样的系统方法,或者自定义category来实现。
4、自定义对象,是深拷贝还是浅拷贝,亦或者是assign,取决于copyWithZone:或者mutableCopyWithZone:的实现。

0 0