深拷贝与浅拷贝
对象拷贝有两种方式:浅拷贝和深拷贝。顾名思义,浅拷贝,并不拷贝对象本身,仅仅是拷贝指向对象的指针;深拷贝是直接拷贝整个对象内存到另一块内存中。
定义
浅拷贝 copy
指针拷贝 - 将指针中的地址值拷贝一份.
只复制指向对象的指针,而不复制引用对象本身。
copy返回的为imutable对象.如果对copy返回值使用mutable对象接口就会crash
深拷贝 mutableCopy
对象拷贝 - 直接拷贝内容,内存中存在了两份独立对象本身。
mutableCopy返回mutable对象
在多层数组中,对于被复制对象的每一层都是对象复制.
单层深拷贝
这种方式只能够提供一层内存拷贝(one-level-deep copy),并非真正的深拷贝。
在多层数组中,对第一层进行内容拷贝,其它层进行指针拷贝.在深复制操作时,对于被复制对象,至少有一层是深复制。
一:对非集合类对象的copy操作:
在非集合类对象中:
1、对不可变对象进行copy操作,是指针复制,mutableCopy操作时内容复制;
不可变的非集合类对象的copy,对象的内存地址没有改变,只是指针的地址改变了。
不可变非集合类对象的mutableCopy,对象的内存地址会改变。
2、对可变对象进行copy和mutableCopy都是内容复制,直接拷贝了对象,对象的内存地址会改变。
进行可变对象的copy时,因为copy默认返回的是不可变的,所以对一个可变的字符串进行拷贝的时候,因为类型转变了,默认对其进行深拷贝,对象的内存地址会改变。
对可变对象进行mutableCopy时,系统要保证旧的对象和新的对象都是可变的,且他们之间不会相互影响,因此对象的内存地址也会改变。
验证:
输出内容: 0x100001080 - 0x100001080 - 0x1003003e0
通过内存地址的打印可以看出,copy NSString,生成的新对象和原string的内存地址是一样的,copy NSMutableString 生成的新对象与原string的内存地址是不一样的。
输出内容: 0x100103920 - 0x326e696769726f75 - 0x1001039b0
可以看出mStringCopy2、stringCopy2和string2的内存地址都是不一样的,此时都是做内容拷贝。
二:集合类对象的copy与mutableCopy
在集合类对象中:
1、不可变对象进行copy操作,是指针拷贝,内存地址不更改.mutableCopy操作时,是内容拷贝,指针地址会更改.
2、可变的集合类对象, copy 与 mutableCopy 都是内容复制。
集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。即这里的内容拷贝仅仅是拷贝"集合类对象"这个对象,集合内的元素仍然是指针拷贝。所以可以定义为是[单层深拷贝]。
验证:
输出结果:0x100600450 - 0x100600450 - 0x1006038e0
可以看出:copy NSArray 生成的对象和原来的array内存地址是一样的,copy NSMutableArray 生成的对象和原来的array内存地址不一样。
输出结果: 0x100106fd0 - 0x100600450 - 0x100107020
可以看出mCopyArray2、copyArray2和array2的内存地址都是不一样的。
三:集合类对象的深复制
集合对象的深复制有两种方法。以数组为例.
1.可以用 initWithArray:copyItems: 将第二个参数设置为YES即可深复制.
使用这种方法深复制,集合里的每个对象都会收到 copyWithZone: 消息。
如果集合里的对象遵循 NSCopying 协议,那么对象就会被深复制到新的集合。
如果对象没有遵循NSCopying协议,而尝试用这种方法进行深复制,会在运行时出错。
copyWithZone: 这种拷贝方式只能够提供一层内存拷贝(one-level-deep copy),而非真正的深复制。
2.将集合进行归档(archive),然后解档(unarchive).
其他
自定义对象的copy
在Objective-C中并不是所有的对象都支持Copy,MutableCopy,遵守NSCopying协议的类才可以发送Copy消息,遵守NSMutableCopying协议的类才可以发送MutableCopy消息。
NSString property属性为什么建议用copy不用strong
1、对源头是NSMutableString的字符串,retain仅仅是指针引用,增加了引用计数器,这样源头改变的时候,用这种retain方式声明的变量(无论被赋值的变量是可变的还是不可变的),它也会跟着改变;而copy声明的变量,它不会跟着源头改变,它实际上是深拷贝。
2、对源头是NSString的字符串,无论是retain声明的变量还是copy声明的变量,当第二次源头的字符串重新指向其它的地方的时候,它还是指向原来的最初的那个位置,也就是说其实二者都是指针引用,也就是浅拷贝。这两者对内存计数的影响都是一样的,都会增加内存引用计数,都需要在最后的时候做处理。
3、其实说白了,对字符串为啥要用这两种方式?还是因为安全问题,比如声明的一个NSString *str变量,然后把一个NSMutableString *mStr变量的赋值给它了,如果要求str跟着mStr变化,那么就用retain;如果str不能跟着mStr一起变化,那就用copy。而对于要把NSString类型的字符串赋值给str,那两都没啥区别。不会影响安全性,内存管理也一样。
Last updated