关于iOS的浅拷贝和深拷贝。
前言
- 浅拷贝:浅拷贝是对内存地址的复制。让目标对象的指针和源对象的指针指向同一片内存空间。原对象的引用计数+1。可以理解成创建了一个指向原对象的新指针而已,并没有创建一个新的对象。
- 深拷贝:深拷贝是指拷贝对象的具体内容,内存地址是自主分配,拷贝结束之后,对象的值是相同的,但是内存地址不一样。两个对象互不影响,互不干涉。
这是我在网上找到的一张图,感谢大佬
浅拷贝
浅拷贝只是对object对象指针进行拷贝,不会开辟新的内存。与数据源指向的是同一内存。例如copyA = [A copy],capyA和A指向的是同一内存,A的值变化,copyA的值也会发生变化。
copy方法做的是浅拷贝。
深拷贝
深拷贝会开辟新的内容,然后对原数据进行复制,新object对象与数据源指向的是不同的内存,对数据源操作不会影响新object对象。例如 mutableCopyA = [A mutableCopy],mutableCopyA和A指向的是两块不同的内存,A的值发生变化不会影响到mutableCopyA的值。
mutableCopy方法做的是深拷贝。
深浅拷贝的区别
深拷贝:重新开辟新的内存空间,完全拷贝该对象的值
浅拷贝:不重新开辟新的内存空间,引用该对象
浅拷贝方式下,如果A对象指向的内存空间上的值改变,也会影响B对象,即造成了安全性问题。深拷贝方式下,则不会有这种问题。那么既然浅拷贝会造成安全性问题,为何还要保留它呢?别急,且看下文。
不同对象类型下的深浅拷贝
- 可变非容器对象,例如NSMutableString,NSMutableArray等
- 不可变非容器对象,例如NSString,NSArray等
- 可变容器对象,例如NSMutableArray,NSMutableDictionary等
- 不可变容器对象,例如NSArray,NSDictionary等
不可变非容器下的拷贝
NSString *originalStr = @"something";
NSMutableString *copyStr = [originalStr copy];
NSMutableString *mutableStr = [originalStr mutableCopy];
NSLog(@"%p %p %p %@ %@",originalStr,copyStr,mutableStr,[copyStr class],[mutableStr class]);
0x1084530f0 0x1084530f0 0x600000269cc0 __NSCFConstantString __NSCFString
我们可以看到
- 不可变非容器对象调用copy进行拷贝,是使用的浅拷贝,返回了该不可变对象指针(通过输出中的__NSCFConstantString可以验证)。原因是不可变对象在初始化时候已经固定了占用的内存大小,不会再进行改变。基于此前提,使用浅拷贝不可变对象时便不必担心原对象会改变而影响到新对象。此时不会产生安全性问题,且不必新申请一个对象空间,节省了内存,这就是上文提到的保留浅拷贝的原因。
- 不可变非容器对象调用mutableCopy进行拷贝,是用的是深拷贝,返回了一个新的可变对象指针(通过输出中的__NSCFString可以验证)。 那既然浅拷贝不可变对象已经实现了保证安全性且节省了内存空间,为何mutableCopy是深拷贝呢?我也不理解,我认为应该是浅拷贝节省的内存空间其实也不多,且深拷贝也可以保证安全性,所以苹果保留了它,给开发者选择
可变飞容器的深浅拷贝
NSMutableString *originalStr = [NSMutableString stringWithString:@"something"];
NSMutableString *copyStr = [originalStr copy];
NSMutableString *mutableStr = [originalStr mutableCopy];
NSLog(@"%p %p %p %@ %@",originalStr,copyStr,mutableStr,[copyStr class],[mutableStr class]);
0x60800007ee80 0xa0c0c60045412da9 0x60800007edc0 NSTaggedPointerString __NSCFString
- 可变非容器对象调用copy进行拷贝,是用的深拷贝,返回了一个NSString的子类对象指针(NSTaggedPointerString其实是NSString的子类,是基于64位cpu下用于优化节省内存的对象)。因为原对象是可变类型,所以为了保证安全性,使用深拷贝。但是有人可能会问,原对象是一个可变类型对象,使用copy后返回的却是不可变对象呢?其实无论是返回可变对象不可变对象,对于新对象都没有任何影响,它只是指向了一个字符串,当新对象改变,本质是指针指向了新字符串。
- 可变对象调用mutableCopy进行拷贝,是用的也是深拷贝,返回了一个新的可变对象指针(通过输出中的__NSCFString可以验证),同样的保证了安全性。