iOSでのディープコピーとシャローコピー

序文

  • 使用される比喻観点浅拷贝は、ファイルが作成されるというもの快捷方式であり、本質的に、これ快捷方式は元のファイルを指します。新しい独立し深拷贝たファイルを形成して、ファイルを実際に実行することです。复制粘贴

  • 指向内存地址視点を使用する浅拷贝と、オブジェクトはコピーされたオブジェクトのメモリアドレスを深拷贝指し、オブジェクトは新しいメモリアドレスを指します。

1.copyおよびmutableCopyメソッド

copy単純に合計mutableCopy浅拷贝合計と見なすことはできないことに注意してください深拷贝

  • の場合copy、コピーされたオブジェクトは不可变对象yes 浅拷贝可变对象yesです。深拷贝
  • の場合mutableCopy、両方深拷贝
  • の場合copy、コピーされたオブジェクトが可変であるかどうかに関係なく、コピーされたオブジェクトは不変です
  • の場合mutableCopy 、コピーされたオブジェクトが変更可能かどうかに関係なく、コピーされたオブジェクトは変更可能です

iOSオブジェクトは、大まかにとに分割容器对象でき非容器对象細分化可变容器对象(NSMutableArray)して、、、、に分割できます不可变容器对象(NSArray)可变非容器对象(NSMutableString,NSMutableDictionary) 不可变非容器对象(NSString,NSDictionary)

copyとmutableCopyの効果を実際に確認してみましょう

  1. 非コンテナ不変オブジェクト
  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
  
复制代码

結論:非容器不可变对象copyはい浅拷贝mutableCopyはい深拷贝

  1. コンテナ以外の可変オブジェクト
 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
复制代码

結論:非容器可变对象、、 copymutableCopy両方深拷贝

这个结论其实解释了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类型的属性。

おすすめ

転載: juejin.im/post/7055644634894057479