iOS 拷贝问题
之前在孙**同学面试过程中,发现一些自己对深浅拷贝、可变不可变拷贝的理解有问题,后来在网上也发现有很多误导性的文章,于是学习总结了一下。
在最开始,我们需要清楚一些关于内存分配方式的基础知识。 一般来说分为栈、堆、静态变量存储区、全局变量存储区、代码区。 前两个大家都懂的。通常将后三个合并称之为静态存储区,存储的是一些全局变量、静态变量、常量、执行代码等。
在Objective-C中,不可变数组、不可变字典以及一些常量字符串,都是分配在这个区域的。 所以在提到深浅拷贝的时候,用NSArray举例子的,只能说对内存分配方式就不清楚,因为对一个不可变数组进行copy操作,它实际上返回的是一个对象,跟深浅拷贝无关,因为都是按照retain来处理的。这也就是很多所谓教程提到的指针拷贝。
对一个不可变数组进行copy,返回的实际是自身,对一个不可变数组进行mutableCopy,会产生一个新的可变数组。
下面先说一下可变拷贝和不可变拷贝,分别遵循NSCopying和NSMutableCopying协议,需要对应实现copyWithZone:方法和mutableCopyWithZone:方法。
分两种情况来讲,一种是系统容器类,一种是自定义类
一、系统容器类
例如NSArray、NSDictionary,它们已经实现了上面两个协议。 对于它们来说,规则很简单,obj2 = [obj1 copy]返回的必然是一个不可变对象,无论obj1是可变对象还是不可变对象。如果obj1是一个不可变对象,那么它们指向同一个对象。 obj2 = [obj1 mutableCopy]返回的必然是一个可变对象,无论obj1是可变对象还是不可变对象。即使obj1也是一个可变对象,它们仍指向不同地址,是两个对象。
二、自定义类
因为copyWithZone:和mutableCopyWithZone:完全由自己来实现,所以代码的不同实现方式,决定了返回对象是什么。
用代码解释一下:
- (id)copyWithZone:(NSZone *)zone{
Element *newElement = [[[Element alloc] init] autorelease];
// newElement.elements = [self.elements retain];
//浅拷贝,因为只是new了一个Element对象,内部的elements数组,依然是指向同一个数组。
newElement.elements = [self.elements copy];
//深拷贝,不但new了一个Element对象,而且内部的elements数组也拷贝了一份,和下一句的区别在于返回的是一个不可变数组。
// newElement.elements = [self.elements mutableCopy];
//深拷贝,不但new了一个Element对象,而且内部的elements数组也拷贝了一份,和上一句的区别在于返回的是一个可变数组。
return newElement;
}
极端一点的例子,例如你直接在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的结果。
网友评论