详细讲解iOS里面深浅拷贝

前沿

属性修饰符包括strong和copy,当问到两者当区别时,一般人都会回答strong是浅拷贝,copy是深拷贝,但是真的是这样吗?

strong和copy修饰不同属性对比

1.strong和copy修饰的字符串

@property (nonatomic, strong) NSString *string2;
@property (nonatomic, copy) NSString *string3;
self.string1 = @"123";
self.string2 = self.string1;
self.string3 = self.string2;
self.string1 = @"456";
NSLog(@"string1: %@ %p",_string1,_string1);
NSLog(@"string2: %@ %p",_string2,_string2);
NSLog(@"string3: %@ %p",_string3,_string3);

2020-01-20 14:15:34.735621+0800 TestOC[3913:560591] string1: 456 0x106e112d8
2020-01-20 14:15:34.735742+0800 TestOC[3913:560591] string2: 123 0x106e10cb8
2020-01-20 14:15:34.735807+0800 TestOC[3913:560591] string3: 123 0x106e10cb8

我们可以发现string2和string3都是浅拷贝,可是为什么string2和string3的值没有变成456呢?这是因为string1赋值456的时候相当于重新初始化了,string1自己的指针地址发生改变,重新指向另一块内存。

那么是不是NSString用strong或copy修饰就没有区别了?其实不然,如果涉及到了NSMutableString,就会看到区别。

@property (nonatomic, copy) NSString *string3;
@property (nonatomic, strong) NSMutableString *string4;

self.string4 = [[NSMutableString alloc] init];
[self.string4 setString:@"111"];
self.string2 = self.string4;
self.string3 = self.string4;
[self.string4 appendString:@"222"];

NSLog(@"string2: %@ %p",_string2,_string2);
NSLog(@"string3: %@ %p",_string3,_string3);
NSLog(@"string4: %@ %p",_string4,_string4);

2020-01-20 14:30:58.853710+0800 TestOC[4092:595407] string2: 111222 0x600002d14c30
2020-01-20 14:30:58.853816+0800 TestOC[4092:595407] string3: 111 0xb200c98b0cb802b3
2020-01-20 14:30:58.853879+0800 TestOC[4092:595407] string4: 111222 0x600002d14c30

此时我们可以发现,当拷贝的对象是一个NSMutableString,用strong修饰的NSString是浅拷贝,用copy修饰的则是深拷贝。此时,为什么NSString使用copy修饰也就可以理解了。使用copy修饰之后,即使属性拷贝来自可变字符串,也会被深拷贝成不可变字符串,也就是源字符串修改之后不会影响到属性字符串,增强了代码的健壮性。

注意:使用copy出来的字符串不管源对象是可变字符串还是不可变字符串,都会变成不可变字符串。因此不可变字符串(NSMutableString)创建成属性的时候不能用copy修饰,需要用strong修饰。

总结:copy出来的字符串一定是不可变字符串,如果传入的是可变字符串,会发生深拷贝为不可变字符串,否则为浅拷贝。strong修饰的都是浅拷贝。

2.copy和mutableCopy出来的字符串

    NSString *string2;
    NSString *string3;
    string2 = [string1 copy];
    string3 = [string1 mutableCopy];
    string1 = @"456"
    NSLog(@"string1: %@ %p",string1,string1);
    NSLog(@"string2: %@ %p",string2,string2);
    NSLog(@"string3: %@ %p",string3,string3);

2020-01-20 15:03:54.962621+0800 TestOC[4372:675991] string1: 456 0x10e6512b8
2020-01-20 15:03:54.962761+0800 TestOC[4372:675991] string2: 123 0x10e650cb8
2020-01-20 15:03:54.962830+0800 TestOC[4372:675991] string3: 123 0x600001233600

可以看出,NSString的mutableCopy也是深拷贝,并且拷贝出来的是一个可变字符串,因为用NSString创建的,尽管本质是一个可变字符串,但是不可以当初可变字符串进行操作,如appendString会报错。

    NSMutableString *string2;
    NSMutableString *string3;
    string2 = [string1 copy];
    string3 = [string1 mutableCopy];
    [string1 appendString:@"456"];
    NSLog(@"string1: %@ %p",string1,string1);
    NSLog(@"string2: %@ %p",string2,string2);
    NSLog(@"string3: %@ %p",string3,string3);

2020-01-20 15:12:01.395989+0800 TestOC[4439:696226] string1: 123456 0x600001775e00
2020-01-20 15:12:01.396136+0800 TestOC[4439:696226] string2: 123 0x94c4c7fe69a3db5b
2020-01-20 15:12:01.396191+0800 TestOC[4439:696226] string3: 123 0x600001774ba0

结论:mutablecopy,一定是深拷贝,拷贝出来的一定是可变字符串,即使传入的是不可变字符串。
注意:NSString和NSMutableString使用不同拷贝,本质可能发生改变。如NSMutableString创建string用copy赋值,此时用NSString的方法赋值会报警告,而用NSMutableString的方法则会直接报错。因此如何使用copy和mutablecopy拷贝需要谨慎使用。

3.strong和copy修饰的数组
当数组为不可变数组,用strong修饰是浅拷贝,如果源数组是不可变数组,则用copy修饰也是浅拷贝,如果源数组是可变数组,则用copy修饰变成深拷贝。
当数组为可变数组,用strong修饰是浅拷贝,用copy修饰都是深拷贝,但是会使数组变成不可变数组,从而不能使用可变数组方法。

总结:针对字符串和数组。
不可变字符串和不可变数组使用copy修饰,这样可以确保当其拷贝源是可变的时候会进行深拷贝,以免修改源造成数据跟随源数据改变。
可变数组和可变字符串使用strong修饰,如果使用copy修饰会将其变成不可变,导致使用可变方法是出错。
当接受值为不可变字符串和不可变数组时,如果接受值为属性,或者源数据为不可变,则可直接用=赋值,否则用copy赋值。
当接受值为可变字符串和可变数组时,推荐使用mutableCopy赋值,因为这样是深拷贝,源值改变不会影响新值。
字典和集合同上

你可能感兴趣的:(详细讲解iOS里面深浅拷贝)