1、简介

copy的目的是产生一个副本,修改源对象不会对副本对象产生影响,修改副本对象不会对源对象产生影响。
iOS提供了两个拷贝方法:
copy,不可变拷贝,产生不可变副本。
mutableCopy,可变拷贝,产生可变副本。
代码举例:

  1. NSString *str1 = [NSString stringWithFormat:@"1"];
  2. NSString *str2 = [str1 copy];
  3. NSMutableString *str3 = [str1 mutableCopy];

str1调用copy得到的str2是不可变字符串,str3是可变字符串。

*可以通过调用appendString:方法检测是否是可变字符串

2、引用计数

当对象调用了copy或者mutableCopy后,引用计数会+1,在MRC环境下当不使用时,需要调用release来释放对象。

  1. NSString *str1 = [NSString stringWithFormat:@"abcdefghijk"];
  2. NSString *str2 = [str1 copy];
  3. NSMutableString *str3 = [str1 mutableCopy];
  4. [str1 release];
  5. [str2 release];
  6. [str3 release];

*Tagged Pointer不需要release

3、深拷贝和浅拷贝

分别打印str1、str2、str3的地址:

  1. NSLog(@"%p %p %p", str1, str2, str3);

打印结果:

  1. ~: 0xeef69fc7a6c0e925 0xeef69fc7a6c0e925 0x1005390b0

可以看到str1和str2的内存地址相同,str3是一个新的内存地址,如下图所示:
image.png
分析:
str1是不可变字符串,copy的目的是保证源对象和副本互不影响,所以str2指向原来的不可变字符串没有违反copy的目的。
str3是可变字符串,为了保证源对象和副本互不影响,所以需要创建一个新的字符串对象来保存str3。
像str3和str2这样的拷贝方式,被称为深拷贝和浅拷贝:
深拷贝:内容拷贝,产生新的对象。
浅拷贝:指针拷贝,不产生新的对象。
再观察一个例子:

  1. NSMutableString *str1 = [[NSMutableString alloc] initWithFormat:@"abc"];
  2. NSString *str2 = [str1 copy];
  3. NSMutableString *str3 = [str1 mutableCopy];
  4. NSLog(@"%p %p %p", str1, str2, str3);

打印结果:

  1. ~: 0x1060348e0 0x46e156054bb85ad9 0x106034e50

可以看到str1、str2、str3分别是三块不同的内存地址,如下图所示:
image.png
分析:
str1是可变字符串,调用copy会得到一个不可变字符串,为了保证源对象和副本互不影响,所以需要创建一个新的不可变字符串底线作为副本。
str3调用mutableCopy会得到一个可变字符串,为了保证源对象和副本互不影响,所以需要创建一个新的可变字符串对象作为副本。
不同类型对象调用Copy和MutableCopy结果如下:
image.png
总结:不可变对象调用copy是浅拷贝,其他情况是深拷贝。

4、copy修饰属性

比如给Person对象添加一个data属性,用copy修饰:

  1. @interface Person : NSObject
  2. @property (nonatomic, copy) NSArray *data;
  3. @end

在其set方法中会调用copy来进行拷贝对象:

  1. - (void)setData:(NSArray *)data {
  2. if (_data != data) {
  3. [_data release];
  4. _data = [data copy];
  5. }
  6. }
  7. - (void)dealloc {
  8. self.data = nil;
  9. [super dealloc];
  10. }

如果属性是一个NSMutableArray类型的数组,就不能用copy修饰,因为在set方法后,会拷贝出来一个不可变数组,往数组中添加数据时会产生崩溃。

*copy用于修饰不可变对象,strong用于修饰可变对象。

一般NSString对象用copy修饰,因为如果属性是用于显示的话,不希望外部修改字符串导致显示变化。而NSDictionary和NSArray一般用strong修饰。

5、自定义类的Copy

如果Person的实例对象想要调用copy方法,得到一个新的对象,就需要遵守NSCopying协议:

  1. @interface Person : NSObject <NSCopying>
  2. @property (nonatomic, copy) NSString *name;
  3. @property (nonatomic, assign) int age;
  4. @property (nonatomic, assign) int weight;
  5. @end

并且实现copyWithZone:方法:

  1. - (nonnull id)copyWithZone:(nullable NSZone *)zone {
  2. Person *p = [[Person allocWithZone:zone] init];
  3. p.name = self.name;
  4. p.age = self.age;
  5. p.weight = self.weight;
  6. return p;
  7. }