(本文根据CocoaChina上的一篇帖子整理,后期附上链接)
最开始,我们需要清楚一些关于内存分配方式的基础知识。
一般来说分为栈、堆、静态变量存储区、全局变量存储区、代码区。
前两个大家都懂的。通常将后三个合并称之为静态存储区,存储的是一些全局变量、静态变量、常量、执行代码等。
在Objective-C中,不可变数组、不可变字典以及一些常量字符串,都是分配在这个区域的,我们先要明确这一点。
所以在提到深浅拷贝的时候,用NSArray举例子的,只能说对内存分配方式就不清楚,因为对一个不可变数组进行copy操作,它实际上返回的是一个对象,跟深浅拷贝无关,因为都是按照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的结果。