iOS copy 和mutablecopy最通俗理解

750 阅读3分钟

  Person* p = [Person new];

  这是oc中最常见的一句代码,它指的是有一个Person对象存在于堆区,p是一个指向这个对象的指针,如下图所示,它的value是堆区的一个地址。而这个指针的地址是&p。

  再来看这样一句代码:

NSArray *arr = [NSArray arrayWithObjects:p, nil];

  NSLog(@"%p",arr);打印出地址值0x2823333c0,这个地址所在的空间里存了一个这样的结构@[address],里边的address 是对象在堆区的地址。

  理解以上两个概念以后,我们再来理解copy的概念。 先说两个基础概念:

    浅复制:不拷贝对象本身,仅仅是拷贝指向对象的指针

    深复制:是直接拷贝整个对象内存到另一块内存中

  再说数组的拷贝,分为NSArray和NSMutableArray. 还是这句代码

NSArray *arr = [NSArray arrayWithObjects:p, nil]
<br> id arr_copy = [arr copy];
<br>id arr_mutableCopy = [arr mutableCopy];

打印结果如下:

  可以看出copy 只是一个简单的指针拷贝,它只是把之前数组的地址又复制了一遍。mutableCopy是深拷贝,它指向了另一个新的地址,这个地址里存的还是对象p的地址。  到这里我有个疑问,arr里的Person对象的一个属性name发生里改变,arr_copy中的person肯定会随着改变,因为他们是指向同一个数组。而arr_mutableCopy呢?他不是深拷贝吗?答案是也会改变。 从打印的结果里我们就可以看出,数组里存储的是对象的地址,就算是深拷贝,也只是把地址复制了一遍,跟对象没有关系。无论你的对象是否实现了NSCopying协议。

 那么假如又这样一个需求:

NSArray *arr = [NSArray arrayWithObjects:p, nil];
id arr_mutableCopy = [arr mutableCopy];

要求arr中p的改变,不影响arr_mutableCopy中p的改变,需要怎么实现?

  只能让P实现NSCopying协议,并且要深拷贝,然后pcopy一个新的对象,放进arr_mutableCopy里。

 再来说一说

NSArray *arr = [NSArray arrayWithObjects:p, nil]
<br> id arr_copy = [arr copy];
<br>id arr_mutableCopy = [arr mutableCopy];

打印如下:

可以看出copy 和mutableCopy 都是深拷贝,但是需要注意的是copy出来的对象是不可变数组。

刚刚说到自定义对象的拷贝,需要注意一下这点: @property (nonatomic, copy) Person *per; 自定义person类没有遵循NSCopying协议,所以会crash。

2019-10-23 10:27:18.341759+0800 图片存取[4774:1961611] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person copyWithZone:]: unrecognized selector sent to instance 0x2821a49c0'

关于字符串的copy是类似的。

NSString * str = @"str";
NSString * copyStr = [str copy];
NSLog(@"%p,%p",str,copyStr);

NSString使用copy是指针拷贝,并没有生成新的对象,原因很明显,因为之前的对象也是不可变对象。

NSMutableString * mutableStr = [NSMutableString stringWithFormat:@"mutableStr"];
  id mutableStr_copy = [mutableStr copy];

可变字符串的拷贝是深拷贝,重新生成了一个新的对象,新的字符串是个不可变字符串,这样就保证了新对象的修改不会影响旧对象。

这也是为什么属性中一个不可变字符串要用copy修饰,如果是赋值是一个NSString对象,用strong或者copy都是一样的,但是如果赋值的是一个NSmutableString的话,如果用strong,那就会出问题,

 NSMutableString * mutableStr = [NSMutableString stringWithFormat:@"mutableStr"];
 self.string = mutableStr;

  这时self.string会随着mutableStr的修改而修改,这显然不是我们想要的效果。

  总结:
  1.对于NSString,NSArray等不可变的属性,用copy修饰,对于NSmutableString,NSMutablearray等可变的数组,用strong指针引用就可以了。

  2.copy出来的数据都是不可变的。对于不可变数据nsarry,nsstirng等他是指针拷贝,而对于NSmutablearray则是深拷贝。而mutablecopy出来的数据都是可变的。而且都是深拷贝。