序文
-
使用される
比喻
観点浅拷贝
は、ファイルが作成されるというもの快捷方式
であり、本質的に、これ快捷方式
は元のファイルを指します。新しい独立し深拷贝
たファイルを形成して、ファイルを実際に実行することです。复制粘贴
-
指向内存地址
視点を使用する浅拷贝
と、オブジェクトはコピーされたオブジェクトのメモリアドレスを深拷贝
指し、オブジェクトは新しいメモリアドレスを指します。
1.copyおよびmutableCopyメソッド
copy
単純に合計mutableCopy
を浅拷贝
合計と見なすことはできないことに注意してください深拷贝
。
- の場合
copy
、コピーされたオブジェクトは不可变对象
yes浅拷贝
、可变对象
yesです。深拷贝
- の場合
mutableCopy
、両方深拷贝
- の場合
copy
、コピーされたオブジェクトが可変であるかどうかに関係なく、コピーされたオブジェクトは不変です - の場合
mutableCopy
、コピーされたオブジェクトが変更可能かどうかに関係なく、コピーされたオブジェクトは変更可能です
iOSオブジェクトは、大まかにとに分割容器对象
でき、非容器对象
細分化可变容器对象(NSMutableArray)
して、、、、に分割できます。不可变容器对象(NSArray)
可变非容器对象(NSMutableString,NSMutableDictionary)
不可变非容器对象(NSString,NSDictionary)
copyとmutableCopyの効果を実際に確認してみましょう
- 非コンテナ不変オブジェクト
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
はい深拷贝
- コンテナ以外の可変オブジェクト
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
复制代码
結論:
非容器可变对象
、、copy
のmutableCopy
両方深拷贝
这个结论其实解释了NSString
对象建议用copy
而不是strong
关键字修饰的问题-数据安全问题。详细见下面。
- 容器不可变对象
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`都是`浅拷贝`
复制代码
- 容器可变对象
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.对于容器内的元素来说,copy
和mutableCopy
都是浅拷贝
- 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
之后得到的对象都不可变
- 自定义对象
- 对于
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关键字修饰的区别
- 对于不可变字符串,
copy
和strong
关键字修饰并无差别 - 对于可变字符串:
copy
修饰的属性赋值时会进行一次copy
操作(对于可变字符串
的拷贝是深拷贝
),也就是为它开辟了新的内存地址。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.strongStr
和self.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
类型的属性。