copy和mutablecopy 源于对数据的复制需求,对于对象类型的数据来说,区别于直接持有这个数据对象的方式,复制可以确保所复制出来的对象不受源对象数据修改的影响。
而oc中的copy对于数值类型,对象类型,及容器数据对象类型都有不同的门道
对于数值类型来说,copy意味着对数据的直接复制,而对于对象类型来说,有浅复制和深复制的区别
在详细讨论深浅复制及copy与mutableCopy之前,先来看几个例子:
NSArray的copy和mutableCopy
以容器类NSArray及NSMutableArray为例
NSMutableArray* ma1 = [[NSMutableArray alloc] initWithObjects:@"v1",@"v2", nil];
__unused NSMutableArray* ma2 = [ma1 mutableCopy];
__unused NSArray* ca1 = [ma1 copy];
__unused NSArray* ca2 = [ca1 copy];
_ca3 = [ca1 copy]; // ca3定义为属性 @property (copy) NSArray* ca3;
[ma1 removeObjectAtIndex:1];
__unused CFIndex i = CFGetRetainCount((__bridge CFArrayRef)ca1);
ca1与ca2及_ca3持有同一个数组,且i值为3,而最终ma1值为 {@"v1"},ca1,ca2,_ca3值为{@"v1",@"v2"}
NSString的copy
int e = 1;
NSString* oa1 = [NSString stringWithFormat:@"abc%d",e];
__unused NSString* ca1 = [oa1 copy];
__unused NSString* ca2 = [oa1 copy];
__unused CFIndex i = CFGetRetainCount((__bridge CFStringRef)oa1);
oa1,ca1,ca2都指向同一个字符串对象,但比较奇怪的是i 值为9223372036854775807,可见其引用计数非法,编译器在编译阶段已经生成了字符串并为其分配了空间,而如果调整成这样
int e = arc4random();
NSString* oa1 = [NSString stringWithFormat:@"abc%d",e];
__unused NSString* ca1 = [oa1 copy];
__unused NSString* ca2 = [oa1 copy];
__unused CFIndex i = CFGetRetainCount((__bridge CFStringRef)oa1);
则i 值为4
深浅拷贝
根据object copying(apple 文档), 支持复制的对象需要实现NSCopying,若有需要还可实现NSMutableCopying,即可以向其发送copy甚至mutableCopy消息。
而深浅复制的不同在于,虽然对于标量数据来说,深浅复制都会对其进行复制,而对于对象类型来说,深复制是完全复制出一个同样的对象,而浅复制则只复制对象的引用,比如像前面NSArray例子中那样只是引用计数增加1
深拷贝和浅拷贝与copy及mutableCopy
需要注意的是 深拷贝和浅拷贝与copy及mutableCopy并没有一对一的关系,并不是说copy即是浅拷贝,mutableCopy即是深拷贝。因为copy消息返回值其实是对应的NSCopying方法 copyWithZone:的返回值,如果覆盖这个方法,你可以仅仅简单地返回原对象,也可以自己实现深拷贝,或者不算深也不算浅的拷贝,这取决于你自己的需求。
在开发中除了自己编写的类,很多情况下是对ios系统类的copy与mutableCopy的使用,了解它们的一些特性也是copy与mutableCopy这个点中很重要的部分。
像前面NSArray的例子,一般来说mutableCopy会进行深拷贝,即会生成新的对象,不管原对象是否是mutable对象,而如果原对象是mutable对象,则copy也会进行深复制,其结果一定是返回一个immutable对象。
嵌套对象
所谓的深复制其实也会两种,一种是对于嵌套的对象同样进行深复制,另一种是对于嵌套的对象进行浅复制
NSMutableDictionary* oa1 = [@{@"k1":@"v1",@"k2":[@"v2" mutableCopy]} mutableCopy];
__unused NSDictionary* ca1 = [oa1 copy];
__unused NSDictionary* ca2 = [ca1 copy];
__unused CFIndex i = CFGetRetainCount((__bridge CFDictionaryRef)ca1);
[[oa1 objectForKey:@"k2"] appendString:@"append"];
比如这个例子中,oa1虽然与ca1及ca2并不是同一个对象,但其中的元素其实是指向的同一个对象,因为最终ca1的值为
{
k1 = v1;
k2 = v2append;
}
也就是说在容器对象这个层面上,深拷贝已经生成了新的容器对象,但对容器对象中包含的子对象却还只是进行了浅复制。