深拷贝
,浅拷贝
,copy
,mutableCopy
,单层拷贝
面试题:
1)什么是深拷贝什么是浅拷贝?
2)对可变对象进行copy是深拷贝还是浅拷贝?
3)为什么给NSString类型属性使用copy修饰,改为strong可以吗?
4)@property (copy) NSMutableArray *array; 这种写法有什么问题?
5)什么是单层拷贝,怎么实现多层拷贝?
6)修饰block属性时,为什么用copy?
浅拷贝:
浅拷贝就是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间,当内存销毁的时候,指向这片内存的几个指针需要重新定义才可以使用,要不然会成为野指针。
深拷贝:
深拷贝是指拷贝对象的具体内容,而内存地址是自主分配的,拷贝结束之后,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉。
【总结】
深拷贝就是内容拷贝,浅拷贝就是指针拷贝。本质区别在于:
使用copy或mutableCopy方法可以创建一个对象的副本。
copy:
需要实现NSCoppying协议;
这些创建的是不可变副本(如NSString、NSArray、NSDictionary);
mutableCopy:
需要先实现NSMutableCopying协议;
创建的是可变副本(如NSMutableString、NSMutableArray、NSMutableDictionary);
NSString *str = @"不可变对象"; NSString *copyStr = [str copy]; NSLog(@"str=%@, 地址:%p",str, str); NSLog(@"copyStr=%@, 地址:%p",copyStr, copyStr); // 打印: 2020-07-05 10:59:46.208267+0800 OCTestDemo[77626:1744180] str=不可变对象, 地址:0x108704330 2020-07-05 10:59:46.208372+0800 OCTestDemo[77626:1744180] copyStr=不可变对象, 地址:0x108704330 // 结论:对不可变对象copy,是浅拷贝。 复制代码
NSString *str = @"不可变对象"; NSString *mtCopyStr = [str mutableCopy]; NSLog(@"str=%@, 地址:%p",str, str); NSLog(@"mtCopyStr=%@, 地址:%p",mtCopyStr, mtCopyStr); // 打印: 2020-07-05 11:01:20.982338+0800 OCTestDemo[77699:1746254] str=不可变对象, 地址:0x10fa86330 2020-07-05 11:01:20.982470+0800 OCTestDemo[77699:1746254] mtCopyStr=不可变对象, 地址:0x600000257fa0 // 结论:对不可变对象mutableCopy,是深拷贝。 复制代码
NSMutableString *str = [NSMutableString stringWithString:@"可变对象"]; NSString *copyStr = [str copy]; NSLog(@"str=%@, 地址:%p",str, str); NSLog(@"copyStr=%@, 地址:%p",copyStr, copyStr); // 打印: 2020-07-05 11:04:04.408830+0800 OCTestDemo[77768:1748628] str=可变对象, 地址:0x60000024c900 2020-07-05 11:04:04.408933+0800 OCTestDemo[77768:1748628] copyStr=可变对象, 地址:0x6000002279a0 // 结论:对可变对象copy,是深拷贝。 复制代码
NSMutableString *str = [NSMutableString stringWithString:@"可变对象"]; NSString *mtCopyStr = [str mutableCopy]; NSLog(@"str=%@, 地址:%p",str, str); NSLog(@"mtCopyStr=%@, 地址:%p",mtCopyStr, mtCopyStr); // 打印: 2020-07-05 11:05:38.838961+0800 OCTestDemo[77828:1750661] str=可变对象, 地址:0x600000256200 2020-07-05 11:05:38.839065+0800 OCTestDemo[77828:1750661] mtCopyStr=可变对象, 地址:0x60000025e8d0 // 结论:对可变对象mutableCopy,是深拷贝。 复制代码
Animal *dog = [[Animal alloc]init]; Animal *cat = [[Animal alloc]init]; Animal *pig = [[Animal alloc]init]; 复制代码
NSArray *arr = @[dog, cat, pig]; NSArray *copyArr = [arr copy]; NSLog(@"arr=%@, 地址:%p",arr, arr); NSLog(@"copyArr=%@, 地址:%p",copyArr, copyArr); // 打印: 2020-07-05 11:10:12.324873+0800 OCTestDemo[77897:1754011] arr=( "<Animal: 0x600000015fc0>", "<Animal: 0x600000015670>", "<Animal: 0x600000015220>" ), 地址:0x6000004598f0 2020-07-05 11:10:12.325063+0800 OCTestDemo[77897:1754011] copyArr=( "<Animal: 0x600000015fc0>", "<Animal: 0x600000015670>", "<Animal: 0x600000015220>" ), 地址:0x6000004598f0 // 结论:对不可变容器对象copy,是浅拷贝,且为单层拷贝。 复制代码
NSArray *arr = @[dog, cat, pig]; NSArray *mtCopyArr = [arr mutableCopy]; NSLog(@"arr=%@, 地址:%p",arr, arr); NSLog(@"mtCopyArr=%@, 地址:%p",mtCopyArr, mtCopyArr); // 打印: 2020-07-05 11:12:30.281808+0800 OCTestDemo[77952:1756412] arr=( "<Animal: 0x604000202810>", "<Animal: 0x6040000178c0>", "<Animal: 0x604000017c70>" ), 地址:0x60400024c7b0 2020-07-05 11:12:30.281930+0800 OCTestDemo[77952:1756412] mtCopyArr=( "<Animal: 0x604000202810>", "<Animal: 0x6040000178c0>", "<Animal: 0x604000017c70>" ), 地址:0x60400024d020 // 结论:对不可变容器对象mutableCopy,是深拷贝,且为单层拷贝。 复制代码
NSMutableArray *arr = [NSMutableArray arrayWithArray:@[dog, cat, pig]]; NSArray *copyArr = [arr copy]; NSLog(@"arr=%@, 地址:%p",arr, arr); NSLog(@"copyArr=%@, 地址:%p",copyArr, copyArr); // 打印: 2020-07-05 11:15:05.863855+0800 OCTestDemo[78006:1758411] arr=( "<Animal: 0x60400001e990>", "<Animal: 0x60400001e400>", "<Animal: 0x60400001e390>" ), 地址:0x60400024b040 2020-07-05 11:15:05.864007+0800 OCTestDemo[78006:1758411] copyArr=( "<Animal: 0x60400001e990>", "<Animal: 0x60400001e400>", "<Animal: 0x60400001e390>" ), 地址:0x60400024af50 // 结论:对可变容器对象copy,是深拷贝,且为单层拷贝。 复制代码
NSMutableArray *arr = [NSMutableArray arrayWithArray:@[dog, cat, pig]]; NSArray *mtCopyArr = [arr mutableCopy]; NSLog(@"arr=%@, 地址:%p",arr, arr); NSLog(@"mtCopyArr=%@, 地址:%p",mtCopyArr, mtCopyArr); // 打印: 2020-07-05 11:16:56.869198+0800 OCTestDemo[78056:1760281] arr=( "<Animal: 0x600000013270>", "<Animal: 0x600000012fb0>", "<Animal: 0x600000013b70>" ), 地址:0x6000004403c0 2020-07-05 11:16:56.869378+0800 OCTestDemo[78056:1760281] mtCopyArr=( "<Animal: 0x600000013270>", "<Animal: 0x600000012fb0>", "<Animal: 0x600000013b70>" ), 地址:0x600000440480 // 结论:对可变容器对象mutableCopy,是深拷贝,且为单层拷贝。 复制代码
- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;
NSMutableArray *arr = [NSMutableArray arrayWithArray:@[dog, cat, pig]]; NSArray *deepCopyArr = [[NSArray alloc]initWithArray:arr copyItems:YES]; NSLog(@"arr=%@, 地址:%p",arr, arr); NSLog(@"deepCopyArr=%@, 地址:%p",deepCopyArr, deepCopyArr); // 打印: 2020-07-05 11:27:19.171118+0800 OCTestDemo[78309:1769460] arr=( "<Animal: 0x600000000df0>", "<Animal: 0x6000000006b0>", "<Animal: 0x600000000cd0>" ), 地址:0x600000254850 2020-07-05 11:27:19.171313+0800 OCTestDemo[78309:1769460] deepCopyArr=( "<Animal: 0x600000000b70>", "<Animal: 0x600000000dc0>", "<Animal: 0x600000000d80>" ), 地址:0x6000002544f0 // 结论:使用initWithArray:copyItems:方法可以达到多层拷贝的效果,但这里也只是双重拷贝,并不是真正意义上的多层拷贝。 // 注意,数组内的元素需要实现NSCopying,NSMutableCopying协议。 复制代码
NSMutableArray *arr = [NSMutableArray arrayWithArray:@[dog, cat, pig]]; NSArray *deepCopyArr = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:arr]]; NSLog(@"arr=%@, 地址:%p",arr, arr); NSLog(@"deepCopyArr=%@, 地址:%p",deepCopyArr, deepCopyArr); // 打印: 2020-07-05 11:38:08.175207+0800 OCTestDemo[78604:1779461] arr=( "<Animal: 0x60000000ab90>", "<Animal: 0x60000000ab70>", "<Animal: 0x600000010bf0>" ), 地址:0x60000044f5a0 2020-07-05 11:38:08.175351+0800 OCTestDemo[78604:1779461] deepCopyArr=( "<Animal: 0x600000010f70>", "<Animal: 0x600000010ef0>", "<Animal: 0x600000010f60>" ), 地址:0x60000044f600 // 结论:使用归档解档方法可以达到多层拷贝的效果。注意,数组内的元素需要实现NSCoding协议。 复制代码
NSString、NSArray、NSDictionary等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;
copy 此特质所表达的所属关系与 strong 类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为 NSString 时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个 NSMutableString 类的实例。
用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。
block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区。在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。
@property (copy) NSMutableArray *array;
这种写法有什么问题?两个问题:
1、添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃。因为 copy 就是复制一个不可变 NSArray 的对象;
2、使用了 atomic 属性会严重影响性能;