iOS深拷贝与浅拷贝(附demo)
浅拷贝
浅拷贝又叫指针拷贝,只拷贝对象指针,不创建新的对象,拷贝对象和原对象都指向同一块内存地址的内容。
什么是深拷贝
深拷贝又叫内容拷贝,深拷贝时系统会开辟新的内存空间,把对象存入新的内存区域,相较于原来对象,拷贝对象是一个新的对象,原对象的改变不影响新对象;
![](https://img.haomeiwen.com/i5130495/82eac63c9c89ef26.png)
思考🤔:以上面例子NSString来说,浅拷贝对象和原对象共用一块内存地址,要是该地址的内容被修改了,拷贝对象对应的值是不是也被动的被更改了?这时候拷贝对象是不是就不安全,怎么解决呢?
大家知道,我们创建NSString属性时候修饰词用的是copy,要是换成strong会怎么呢?请看:
@property (nonatomic, strong)NSString *strCoptTest;/**<wct20191023 一个用strong修饰的NSString对象*/
![](https://img.haomeiwen.com/i5130495/72c9cab0b51b7a28.png)
可以看到我们修改strCoptTest,拷贝对象也被修改了。如果我们用copy修饰NSString属性则不会出现这问题,为什么copy修饰不会出现这个问题呢?
因为copy修饰,在调用对象的set方法会执行copy操作,执行NSCopying协议内的方法而此处拷贝对象strOriginal是NSMutableString类型,NSMutableString的copy操作是深拷贝,返回不可变的NSString对象
点进NSString去我们发现它实现了NSCopying协议
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>
参照其他对象,在此猜测当copy对象为NSMutableString对象,NSCopying协议实现里新建了副本对象(等于深拷贝,此时拷贝对象指向一个新地址)
印证
对象用copy修饰
@property (nonatomic, copy)NSString *strCoptTest;/**<wct20191023 一个用copy修饰的NSString对象*/
运行结果:可以看到地址不一样,验证了我们上面说的对NSMutableString执行copy是深拷贝
![](https://img.haomeiwen.com/i5130495/98f20625a13f0c0c.png)
同理,在一个strong修饰的NSArray的情况
@property (nonatomic, strong)NSArray *arrList;/**<wct20191024 arrList*/
可以看到我们修改arrMut对象后,self.arrList对象的值被动被修改了,这不是我们想要的结果。
![](https://img.haomeiwen.com/i5130495/ae92df6b76a3b73b.png)
用copy修饰后,对象arrMut的set方法会执行copy操作,copy的对象是arrMut,是NSMutableArray类型,可以看到新开辟了内存,是深拷贝,所以NSMutableArray的copy是深拷贝,对NSArray修饰应该用copy关键字
-(void)setArrList:(NSArray *)arrList
{
_arrList = [arrList copy];//此处arrList对应代码里的arrMut,是NSMutableArray类型,copy为深拷贝
}
执行效果,可以看到避免了被动修改的情况
![](https://img.haomeiwen.com/i5130495/a9bf941ff4b78e96.png)
那copy就能避免对象被动被修改了吗?答案是否定的,接着往下看
集合对象的拷贝问题
新建Person类
@interface Person : NSObject
@property (nonatomic, strong)NSString *name;/**<wct20191024 name*/
@property (nonatomic, strong)NSString *age;/**<wct20191024 age*/
@property (nonatomic, strong)NSString *gender;/**<wct20191024 gender*/
发现无论是strong还是copy修饰都不安全,和NSString例子同样情况,如下
@property (nonatomic, copy)NSArray *arrList;/**<wct20191024 arrList*/
![](https://img.haomeiwen.com/i5130495/e5f32c22b6c73d77.png)
可以看到arrMut的第一个成员变量person1的name被修改为haha,self.arrList的第一个成员的名字也变为了haha,这不是我们想要的,为什么呢?
虽然数组对象通过copy修饰实现了深拷贝(绿色圈),但是数组内的对象还是浅拷贝(红色圈),修改被拷贝数组内成员对象的值,等于也修改了拷贝对象成员的值
解决方案
上面说到是因为数组内对象没有实现深拷贝,所以我们的目的就是把数组内对象也实现深拷贝,正好NSArray也提供了对应方法:
self.arrList = [[NSArray alloc] initWithArray:arrMut copyItems:YES];//赋值,且深拷贝成员对象
注意自定义的对象要使用copy的遵循NSCopying协议,实现copyWithZone方法
@interface Person : NSObject<NSCopying>
-(id)copyWithZone:(NSZone *)zone
{
Person *copy = [[Person alloc] init];
if (copy) {
copy.name = [self.name copyWithZone:zone];
copy.age = [self.age copyWithZone:zone];
copy.gender = [self.gender copyWithZone:zone];
}
return copy;
}
再看实现效果:如下,可看到数组内成员对象也实现了深拷贝,避免了数组内容被动被修改的情况
![](https://img.haomeiwen.com/i5130495/f1cc722e940a0b60.png)
总结
1.不可变类型属性修饰词用copy;
2.对集合类对象,copy时内部成员变量也要深拷贝;
3.自定义的对象要使用copy的遵循NSCopying协议,实现copyWithZone方法;
各类型对象copy关系
1.不可变对象的copy都是浅拷贝,副本类型为不可变类型;
2.不可变对象mutableCopy是深拷贝,副本类型为可变类型;
3.可变对象的copy都是是深拷贝,副本类型为不可变类型;
4.可变对象mutableCopy是深拷贝,副本类型为可变类型;
copy和strong关系
1.copy修饰的属性,在set方法会进行copy操作,而strong只是赋值;
2.浅拷贝会对原对象指针进行引用,原对象引用计数retainCount会+1,相当于strong;
3.深拷贝等于重新创建一个新对象,不会对原对象有引用关系,原对象retainCount不变;
对于可变类型的属性,不要用copy修饰,因为在赋值给它不可变的mutableCopy对象时,会走属性内的copy,导致属性接收到的是不可变类型,调用可变类型的方法会崩溃,如下图
copy修饰可变属性的问题
但是用strong修饰是浅拷贝,会存在原对象被修改,当前对象也被动被改的情况,所以对可变对象赋值时候手动调用mutableCopy;