Deep Copy and Shallow Copy in iOS

foreword

  • 比喻The point of view used 浅拷贝is that a file is created 快捷方式, and in essence this 快捷方式points to the original file. 深拷贝is to do real to the file 复制粘贴, forming a new independent file.

  • Using 指向内存地址the point of view, 浅拷贝the object points to the memory address of the copied object, and 深拷贝the object points to a new memory address.

1. copy and mutableCopy methods

It should be noted that we cannot simply regard copysum mutableCopyas 浅拷贝sum 深拷贝.

  • For copy, the copied object is 不可变对象yes 浅拷贝, 可变对象yes深拷贝
  • For mutableCopy, both深拷贝
  • For copy, regardless of whether the copied object is mutable or not, the copied object is immutable
  • For mutableCopy , regardless of whether the copied object is mutable, the copied object is mutable

iOS objects can be roughly divided into 容器对象and 非容器对象, subdivided, can be divided into 可变容器对象(NSMutableArray), 不可变容器对象(NSArray), 可变非容器对象(NSMutableString,NSMutableDictionary), 不可变非容器对象(NSString,NSDictionary).

Let's actually verify the effect of copy and mutableCopy

  1. non-container immutable objects
  NSString *str1 = @"非容器不可变对象";
  NSString *str2 = [str1 copy];
  NSString *str3 = [str1 mutableCopy];
        
  NSLog(@"str1:%p class:%@",str1,[str1 class]);
  NSLog(@"str2:%p class:%@",str2,[str2 class]);
  NSLog(@"str3:%p class:%@",str3,[str3 class]);

 //  打印结果
  str1:0x105718738     class:__NSCFConstantString
  str2:0x105718738     class:__NSCFConstantString
  str3:0x60400024eb80  class:__NSCFString
  
复制代码

Conclusion: For 非容器不可变对象, copyyes 浅拷贝, mutableCopyyes深拷贝

  1. non-container mutable objects
 NSMutableString *str1  = [NSMutableString stringWithFormat:@"非容器可变对象"];
 NSMutableString *str2 = [str1 copy];
 NSMutableString *str3 = [str1 mutableCopy];
        
 NSLog(@"str1:%p class:%@",str1,[str1 class]);
 NSLog(@"str2:%p class:%@",str2,[str2 class]);
 NSLog(@"str3:%p class:%@",str3,[str3 class]);
 
 // 打印结果
 str1:0x600000251be0 class:__NSCFString
 str2:0x600000251010 class:__NSCFString
 str3:0x600000251c40 class:__NSCFString
复制代码

Conclusion: For 非容器可变对象, copy, mutableCopyboth深拷贝

这个结论其实解释了NSString对象建议用copy而不是strong关键字修饰的问题-数据安全问题。详细见下面。

  1. 容器不可变对象
NSArray *array1 = [NSArray arrayWithObjects:@"非容器不可变对象",[NSMutableString stringWithFormat:@"非容器可变对象"], nil];
NSArray *array2 = [array1 copy];
NSArray *array3 = [array1 mutableCopy];
        
NSLog(@"array:%p  copyArray:%p  mutableCopyArray:%p",array1,array2,array3);
NSLog(@"array1[0]:%p   array1[1]:%p  ",array1[0] , array1[1]);
NSLog(@"array2[0]:%p   array2[1]:%p  ",array2[0] , array2[1]);
NSLog(@"array3[0]:%p   array3[1]:%p  ",array3[0] , array3[1]);

 //  打印结果
array:0x6040002312a0  copyArray:0x6040002312a0  mutableCopyArray:0x6040002544f0
array1[0]:0x105718738   array1[1]:0x60400024eb80
array2[0]:0x105718738   array2[1]:0x60400024eb80
array3[0]:0x105718738   array3[1]:0x60400024eb80

复制代码

结论: 1. 对于容器不可变对象来说, copy浅拷贝, mutableCopy深拷贝

             2. 对于容器内的元素来说,`copy`和`mutableCopy`都是`浅拷贝` 
复制代码
  1. 容器可变对象
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"非容器不可变对象",[NSMutableString stringWithFormat:@"非容器可变对象"], nil];
NSMutableArray *array2 = [array1 copy];
NSMutableArray *array3 = [array1 mutableCopy];
        
NSLog(@"array1:%p  array2:%p  array3:%p",array1,array2,array3);
NSLog(@"array1[0]:%p   array1[1]:%p ",array1[0], array1[1]);
NSLog(@"array2[0]:%p   array2[1]:%p ",array2[0], array2[1]);
NSLog(@"array3[0]:%p   array3[1]:%p ",array3[0], array3[1]);

// 打印结果
array1:0x604000254820  array2:0x604000231a00  array3:0x6040002545b0
array1[0]:0x105718738   array1[1]:0x6040002544f0
array2[0]:0x105718738   array2[1]:0x6040002544f0
array3[0]:0x105718738   array3[1]:0x6040002544f0

复制代码

结论:1. 对于容器可变对象来说,copy,mutableCopy都是深拷贝

2.对于容器内的元素来说,copymutableCopy都是浅拷贝

  1. copy 与mutableCopy 后得到的对象
NSString *str1 = @"非容器不可变对象";
NSMutableString *str2 = [str1 mutableCopy];
[str2 appendString:@"111"];
NSLog(@"str2:%@",str2);

// 打印结果
str2:非容器不可变对象111

/******************************************************************************/

NSMutableString *str1 = [NSMutableString stringWithFormat:@"非容器可变对象"];
NSMutableString *str2 = [str1 copy];
[str2 appendString:@"111"];

// 执行[str2 appendString:@"111"]报错
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:'
复制代码

结论:1. 不论被拷贝对象是否可变,执行了mutableCopy之后得到的对象都是可变的

2.不论被拷贝对象是否可变,执行了copy之后得到的对象都不可变

  1. 自定义对象
  • 对于copy,自定义类需要添加NSCopying协议,并实现对应的copyWithZone:方法。
  • 对于mutableCopy,自定义类需要添加NSMutableCopying协议,并实现对应的mutableCopyWithZone:方法。
@interface Singer() <NSCopying>
@property (nonatomic, strong) NSString *name;
@end

- (id)copyWithZone:(NSZone *)zone {
  
    Singer *singer = [[Singer allocWithZone:zone] init];
    singer.name = self.name; 
    return singer;
}
复制代码

二. NSString 对象copy和strong关键字修饰的区别

  • 对于不可变字符串,copystrong关键字修饰并无差别
  • 对于可变字符串:
    1. copy修饰的属性赋值时会进行一次copy操作(对于可变字符串的拷贝是深拷贝),也就是为它开辟了新的内存地址。
    2. strong 修饰的属性则不会进行深拷贝操作,不会开辟新的内存地址。
NSString *str = @"我是个不可变字符串";
self.strongStr = str;
self.copyedStr = str;
       
NSLog(@"str:%p, strongStr:%p, copyedStr:%p",str,self.strongStr,self.copyedStr);

// 打印结果
str:0x10cd90978, strongStr:0x10cd90978, copyedStr:0x10cd90978

复制代码

当字符串是不可变类型时,self.strongStrself.copyedStr 并无区别,指向的都是str 的内存地址。

NSMutableString *str = [NSMutableString stringWithFormat:@"我是个可变字符串"];
self.strongStr = str;
self.copyedStr = str;
[str appendString:@"哈哈哈"];

NSLog(@"str:%p, strongStr:%p, copyedStr:%p",str,self.strongStr,self.copyedStr);
NSLog(@"str:%@, strongStr:%@, copyedStr:%@",str,self.strongStr,self.copyedStr);

// 打印结果
str:0x6000002507d0, strongStr:0x6000002507d0, copyedStr:0x600000251760
str:我是个可变字符串哈哈哈, strongStr:我是个可变字符串哈哈哈, copyedStr:我是个可变字符串
复制代码

当字符串是可变类型时,self.copyedStr指向了新的内存地址,self.copyedStr指向的依旧是str的内存地址。

strong关键字修饰NSString类型的属性究竟会造成什么问题呢? 当我们对strongStr属性赋值完毕之后,对str进行修改,由于指向的是同一个内存地址,self. strongStr的值也发生了变化。strongStr在我们不知道的情况下发生了修改行为,这是不安全的。所以在类型不明确的情况下,一般建议使用copy关键字修饰NSString类型的属性。

Guess you like

Origin juejin.im/post/7055644634894057479