iOS-copy

366 阅读4分钟

有关copy的几个概念

1. 拷贝

  • 就是复制,产生副本,让原对象和副本相互独立,互不影响

2. 不可变拷贝(copy)

copy方法,无论原对象是否可变,都产生不可变副本

3. 可变拷贝(mutableCopy)

mutableCopy方法,无论原对象是否可变,都产生可变副本

举个例子

NSString *str = @"hello world";
[str copy]; // 拷贝出内容为hello world的NSString类型的字符串
[str mutableCopy]; // 拷贝出内容为hello world的NSMuttableString类型的字符串

4. 深拷贝

内容拷贝,会产生新的对象

  • 拷贝出来的对象与原对象的地址不一样,修改拷贝对象的值对原对象不会有影响
  • 深拷贝就是拷贝整个对象内容到另一块内存中

5. 浅拷贝

指针拷贝,不产生新的对象

  • 拷贝出来的对象与原对象的地址一致,修改拷贝对象的值对原对象会有影响
  • 浅拷贝就是在原对象的基础上引用计数+1

注意点

由上可知:copy和深拷贝是两个概念!!!

  • 原对象不可变时,copy方法就是浅拷贝
  • 原对象可变时,copy方法就是深拷贝
  • mutableCopy方法无论原对象可变还是不可变,都是深拷贝

安全性

1. 不希望对象无意间被修改

举个例子

@property(nonatomic,strong)NSString *str; //strong修饰

NSMutableString *mutableStr = [NSMutableString stringWithFormat:@"123"];
self.str = mutableStr;
NSLog(@"%@----%p", self.str,self.str);
NSLog(@"%@----%p", mutableStr,mutableStr);
[mutableStr appendString:@"456"]; //修改mutableStr
NSLog(@"%@----%p", self.str,self.str);
NSLog(@"%@----%p", mutableStr,mutableStr);

输出结果
2021-04-04 11:34:23.227973+0800 copy示例[2344:73458] 123----0x6000035c35a0
2021-04-04 11:34:23.228096+0800 copy示例[2344:73458] 123----0x6000035c35a0
2021-04-04 11:34:23.228187+0800 copy示例[2344:73458] 123456----0x6000035c35a0
2021-04-04 11:34:23.228268+0800 copy示例[2344:73458] 123456----0x6000035c35a0

可以看到当你修改mutableStr时,会导致str也被改变,显然有时候是我们并不希望看到的,如果我们的需求是当改变mutableStrstr不会被影响,则应当使用copy修饰

上述代码第一行改为@property(nonatomic,copy)NSString *str;,其余部分不变,结果如下

2021-04-04 11:38:04.092095+0800 copy示例[2388:76204] 123----0xc65d21ed94e36c08
2021-04-04 11:38:04.092227+0800 copy示例[2388:76204] 123----0x600000943ed0
2021-04-04 11:38:04.092324+0800 copy示例[2388:76204] 123----0xc65d21ed94e36c08
2021-04-04 11:38:04.092471+0800 copy示例[2388:76204] 123456----0x600000943ed0

属性由strong修饰时,将可变字符串赋值给该属性,那么属性和可变字符串指向的是同一对象,修改可变字符串时,属性也会被修改,为了防止属性在其他地方被不经意间修改,我们使用copy修饰符

2. 对象的可变和不可变性质发生了改变,导致使用方法出错

举个例子

@property(nonatomic,copy)NSMutableString *mutableString; //copy修饰的可变字符串

NSMutableString *str = [NSMutableString stringWithFormat:@"1234"]; //可变字符串1234
self.mutableString = str; //将str赋给mutableString
NSLog(@"string:%@ --- mutableString:%@",str,self.mutableString);
[self.mutableString appendString:@"5678"];
NSLog(@"string:%@ --- mutableString:%@",str,self.mutableString);

输出如下
string:1234 --- mutableString:1234
unrecognized selector sent to instance

在这里其实出错的关键点在于 self.mutableString = str;[self.mutableString appendString:@"5678"];

  • self.mutableString = str; 这行代码其实就是 [self.mutableString setMutableString:str];,具体实现如下
-(void)setMutableString:(NSMutableString *)mutableString{
    if (_mutableString != mutableString) {
        [_mutableString release];
        _mutableString = [mutableString copy];
    }
}

主要的代码_mutableString = [mutableString copy];

  • 其中的mutableString是传入进来的参数str
  • 使用copy方法是因为属性由copy修饰
  • 因为str本身是可变字符串,因此可变字符串copy得到的值是不可变对象,即[mutableString copy]得到的是不可变对象
  • mutableString本身虽然一开始定义的是可变字符串,但是不可变对象赋值给了_mutableString,因此其也还是一个不可变字符串了

而下面又对如今不可变字符串mutableString进行了[self.mutableString appendString:@"5678"];

  • 不可变对象是没有appendString方法的,所以报错unrecognized selector sent to instance

可变字符串copy后得到的是不可变字符串,即使是赋值给可变属性,属性其实是不可变的,但我们以为该属性是可变的,因此我们可能会使用错误的方法


参考博客

iOS基础知识(一) copy