前言
说道拷贝大家可能联想到深拷贝和浅拷贝概念,然而在swift却很少涉及宝贝问题,即使swift下依然有copy和mutableCopy方法。但终其原因,创建单纯的swift类并不需要继承NSObject,而是使用swift类,另外很多牵扯拷贝问题的数组和字典,在swift对应于Array和Dictionary,不同于NSArray 和NSDictionary,swift的Array和Dictionary是值类型,赋值后并不需要担心其拷贝问题。
所以主要Objective-C中的深拷贝和浅拷贝。
什么是深拷贝?什么是浅拷贝???
深拷贝是深度拷贝,是拷贝一个实例对象到一个新的内存地址,
浅拷贝只是简单拷贝一个实例对象的指针。
从图中可以看到,集合的
浅拷贝后的数组Array2和Array1 指向同一段内存区域而深拷贝Array2和Array1 分别指向不同的内存区域
从这一点来看我们刚刚所说的是正确的,
非集合拷贝
不可变字符串(NSString)
代码
NSString *string = @"123"; NSString *stringCopy = [string copy]; NSMutableString *stringMutable = [string mutableCopy]; NSLog(@"string addrerss:%p",string); NSLog(@"stringCopy addrerss:%p",stringCopy); NSLog(@"stringMutable addrerss:%p",stringMutable);
打印结果

- NSString 进行copy 只是对其指针拷贝
- NSString 进行 mutableCopy 是真正创建一份新的对象
不可变的字符串是存在内存常量,因此可以看到两处地址相差甚远
可变字符串(NSMutableString)
NSMutableString *mString = [[NSMutableString alloc]initWithString:@"123"]; NSString *mStringCopy = [mString copy]; NSString *mStringMutable = [mString mutableCopy]; NSLog(@"mString addrerss:%p",mString); NSLog(@"mStringCopy addrerss:%p",mStringCopy); NSLog(@"mStringMutable addrerss:%p",mStringMutable); NSLog(@"mStringCopy is mutable ? %@",[mStringCopy isKindOfClass:[NSMutableString class]] ? @"YES" : @"NO"); NSLog(@"mStringMutable is mutable ? %@",[mStringMutable isKindOfClass:[NSMutableString class]] ? @"YES" : @"NO");

- NSMutableString 的copy 变成是一个不可变的字符串
- mutableCopy 返回的是一个可变的字符串
- NSMutableString 无论copy还是mutableCopy 地址都不一样
总结

集合类对象拷贝(copy/mutableCopy)
集合类不可变对象
NSString *str1 = @"hello world"; NSMutableString *str2 = [NSMutableString stringWithString:@"hello world"]; NSArray *array1 = [NSArray arrayWithObjects: str1, str2, nil]; NSArray *array2 = [array1 copy]; NSArray *array3 = [array1 mutableCopy]; NSLog(@"\n array1 = %p class = %@ \n", array1, [array1 class]); NSLog(@"\n array2 = %p class = %@ \n", array2, [array2 class]); NSLog(@"\n array3 = %p class = %@ \n", array3, [array3 class]); NSLog(@"\n\n======== 元素是String ======== "); NSLog(@"\n obj0 = %p class = %@ \n", array1[0], [array1[0] class]); NSLog(@"\n obj0 = %p class = %@ \n", array2[0], [array2[0] class]); NSLog(@"\n obj0 = %p class = %@ \n", array3[0], [array3[0] class]); NSLog(@"\n\n======== 元素是mutableString ========"); NSLog(@"\n obj1 = %p class = %@ \n", array1[1], [array1[1] class]); NSLog(@"\n obj1 = %p class = %@ \n", array2[1], [array2[1] class]); NSLog(@"\n obj1 = %p class = %@ \n", array3[1], [array3[1] class]);
输出
2021-05-08 17:34:32.+0800 AlgorithmDemo[39309:] array1 = 0x class = __NSArrayI array2 = 0x class = __NSArrayI array3 = 0x class = __NSArrayM ======== 元素是String ======== obj0 = 0x class = __NSCFConstantString obj0 = 0x class = __NSCFConstantString obj0 = 0x class = __NSCFConstantString ======== 元素是mutableString ======== obj1 = 0xb0 class = __NSCFString obj1 = 0xb0 class = __NSCFString obj1 = 0xb0 class = __NSCFString
由打印可得:
- 集合类不可变对象的copy为浅拷贝
- mutableCopy为深拷贝
- 无论copy还是mutableCopy集合内元素都是浅拷贝
集合类可变对象
NSString *str1 = @"hello world"; NSMutableString *str2 = [NSMutableString stringWithString:@"hello world"]; NSMutableArray *array1 = [NSMutableArray arrayWithObjects: str1, str2, nil]; NSMutableArray *array2 = [array1 copy]; NSMutableArray *array3 = [array1 mutableCopy]; NSLog(@"\n array1 = %p class = %@ \n", array1, [array1 class]); NSLog(@"\n array2 = %p class = %@ \n", array2, [array2 class]); NSLog(@"\n array3 = %p class = %@ \n", array3, [array3 class]); NSLog(@"\n\n======== 元素是mutableString ========"); NSLog(@"\n obj0 = %p class = %@ \n", array1[0], [array1[0] class]); NSLog(@"\n obj0 = %p class = %@ \n", array2[0], [array2[0] class]); NSLog(@"\n obj0 = %p class = %@ \n", array3[0], [array3[0] class]); NSLog(@"\n\n======== 元素是String ======== "); NSLog(@"\n obj1 = %p class = %@ \n", array1[1], [array1[1] class]); NSLog(@"\n obj1 = %p class = %@ \n", array2[1], [array2[1] class]); NSLog(@"\n obj1 = %p class = %@ \n", array3[1], [array3[1] class]);
输出:
2021-05-08 17:40:01.+0800 AlgorithmDemo[40202:] array1 = 0xe0 class = __NSArrayM array2 = 0x class = __NSArrayI array3 = 0x class = __NSArrayM ======== 元素是mutableString ======== obj0 = 0x class = __NSCFConstantString obj0 = 0x class = __NSCFConstantString obj0 = 0x class = __NSCFConstantString ======== 元素是String ======== obj1 = 0x class = __NSCFString obj1 = 0x class = __NSCFString obj1 = 0x class = __NSCFString
结论
- 集合类可变对象的mutableCopy和copy都是深拷贝
- 但集合内元素是浅拷贝
总结
非集合类对象只有不可变对象的copy是浅拷贝,其它都是深拷贝 集合类且集合内元素都是浅拷贝

也可换个方式总结:
- copy: 对不可变对象是浅拷贝,可变对象是深拷贝
- mutableCopy: 始终是深拷贝
- 无论深浅拷贝,集合对象内元素都是浅拷贝
集合里面元素拷贝(copyItems: )
集合浅拷贝
集合浅拷贝的方式很多,当创建一个浅拷贝时,之前集合里面的每个对象都会收到retain的消息,使其引用计数加1,并将其指针拷贝到新的集合中。
NSArray *shaoolwCopyArray = [someArray copyWithZone:nil]; NSDictionary *shaoolwCopyDict = [[NSDictionary alloc]initWithDictionary:someDict copyItems: NO];
数组浅拷贝
代码
NSArray *someArray = @[@222]; NSArray *shaoolwCopyArray = [someArray copyWithZone:nil]; NSLog(@"someArray addrerss:%p",someArray); NSLog(@"shaoolwCopyArray addrerss:%p",shaoolwCopyArray);
打印后的地址

浅拷贝的数组所指向的地址是一样的
这是因为对于数组我们只调用了它的copyWithZone方法,由于是不可变的数组,返回了自身,所以浅拷贝前后不可变数组内存地址不变
字典浅拷贝
代码
NSDictionary *someDict = @{
@"11":@"22"}; NSDictionary *shaoolwCopyDict = [[NSDictionary alloc]initWithDictionary:someDict copyItems: NO]; NSLog(@"someDict addrerss:%p",someDict); NSLog(@"shaoolwCopyDict addrerss:%p",shaoolwCopyDict);
打印后的地址

字典所指向的内存地址发生了变化,其内部元素的地址不变。
而对于字典来说,shaoolwCopyDict是通过alloc和init 创建的,因此在内存中开辟了一段新的内存空间,所以浅拷贝前后字典的内存地址发生了变化.但对于之前字典中的对象,只是拷贝其内存地址,
如果代码改变一下
NSDictionary *someDict = @{
@"11":@"22"}; NSDictionary *shaoolwCopyDict = [someDict copy];
打印结果

NSDictionary 它是不可变的使用copy 之后返回自身
浅拷贝总结
浅拷贝并不是自身的浅拷贝,而是对于其内部元素的浅拷贝
集合深拷贝
对于深拷贝将copyItems:换成YES就是深拷贝了。
在深拷贝中,系统会向集合中的每一个元素对象发送一个copyWithZone:消息,改消息是来自与NSCopying协议,如果有对象没有实现改协议方法,那么就会导致崩溃,如果实现了改方法,那么会根据该方法的具体实现。
数组深拷贝
数组深拷贝(元素不可变NSString)
代码
NSString *str = @"2222"; NSArray *someArray = @[str]; NSArray *shaoolwCopyArray = [someArray copyWithZone:nil]; NSArray *deepCopyArray = [[NSArray alloc]initWithArray:someArray copyItems:YES]; NSLog(@"someArray addrerss:%p",someArray); NSLog(@"shaoolwCopyArray addrerss:%p",shaoolwCopyArray); NSLog(@"deepCopyArray addrerss:%p",deepCopyArray); NSLog(@"someArray[0] addrerss:%p",someArray[0]); NSLog(@"shaoolwCopyArray[0] addrerss:%p",shaoolwCopyArray[0]); NSLog(@"deepCopyArray[0] addrerss:%p",deepCopyArray[0]);
猜测结果shaoolwCopyArray和deepCopyArray应该是不一样的,这是因为deepCopyArray是通过alloc ,init 创建的,但最起码这两个数组的首元素地址是不一样的,运行后发现结果
运行结果

前三行与我们猜测的一致。但后面三行却 打印了相同的地址,然后后面也打印了一样的,明明采用了深浅拷贝,出现的结果确是相同的内存地址。
原因
集合类型的深拷贝会对每个元素调用copyWithZone:方法,
这就意味着刚刚后面三行打印的结果取决于该方法,在深拷贝时对于第一个元素调用了
NSString的copyWithZone
:,但由于NSString是不可变的,对于其深拷贝创建一个新的内存是无意的,所以我们也可以猜测NSString的copyWithZone:返回的是self.所以浅拷贝时直接拷贝元素地址,而深拷贝时通过copyWithZone:方法来获取元素地址,两个结果是一样的。
数组深拷贝(元素可变NSMutableString)
将数组里面的NSString元素改变为NSMutableString
代码
NSMutableString *str = [[NSMutableString alloc]initWithString:@"2222"]; NSArray *someArray = @[str]; NSArray *shaoolwCopyArray = [someArray copyWithZone:nil]; NSArray *deepCopyArray = [[NSArray alloc]initWithArray:someArray copyItems:YES]; NSLog(@"someArray addrerss:%p",someArray); NSLog(@"shaoolwCopyArray addrerss:%p",shaoolwCopyArray); NSLog(@"deepCopyArray addrerss:%p",deepCopyArray); NSLog(@"someArray[0] addrerss:%p",someArray[0]); NSLog(@"shaoolwCopyArray[0] addrerss:%p",shaoolwCopyArray[0]);
运行结果

集合完全拷贝
就是对对象的每一层都是重新创建的实例变量,不存在指针拷贝
例子(数组的归档解挡)
在对数组的归档解挡时就是完全深拷贝
NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
三种集合拷贝总结
浅拷贝
在浅拷贝操作时,对于被拷贝对象的每一层都是指针拷贝
深拷贝
在深拷贝操作时,对于被拷贝对象,至少有一层是深拷贝
完全拷贝
在完全拷贝操作时,对于被拷贝对象,每一层都是深拷贝
面试题
copy 修饰符
一般用于修饰字符串和block以及一些字典和数组。
copy 修饰(字符串/字典/数组)
看下面的例子

打印结果是NO,如果把copy换成strong就变成YES了
copy修饰符分析
在使用copy时,会对属性的setter方法进行判断,对属性copy,根据属性是否为可变,
- 可变返回一个新的不可变对象,即创建一个新不可变字符串
- 而对于不可变返回自身self,即为可变字符串。
strong修饰符分析
strong 则是对其引用,并没有执行copy方法
数组,字典,字符串,原理是一样的
对于NSString 作为不可变的字符串来说
NSString 不可变的对象,使用strong和copy是一样的
copy 修饰(block)
在ARC中如果引用外部变量赋值时便会自动拷贝到内存中
ARC 下使用block 使用copy/strong无异
这是为什么,数组浅拷贝不变,而字典浅拷贝发生了变化???
案例题(copyItems: 方法的讨论)
假设有一个Person类它有一个属性name,放入可变数组/或者不可变数组,数组发生深浅拷贝
代码
Person *a = [[Person alloc]init]; a.name = @"阿里巴巴"; Person *b = [[Person alloc]init]; b.name = @"今日头条"; NSMutableArray *mutA = [NSMutableArray arrayWithObjects:a,b, nil]; NSMutableArray *mutB = [mutA mutableCopy]; // Personerson @interface Person : NSObject @property (nonatomic, copy) NSString *name; @end
mutA 与 mutB 地址是否一样
NSLog(@"mutA addrerss:%p",mutA); NSLog(@"mutB addrerss:%p",mutB);
答案
mutA addrerss:0xbba0 mutB addrerss:0xbe10 深拷贝数组地址不一样
mutA 与 mutB 里面元素的地址是否一样
NSLog(@"mutA[0] addrerss:%p",mutA[0]); NSLog(@"mutB[0] addrerss:%p",mutB[0]);
答案
mutA[0] addrerss:0xf6c3d0 mutB[0] addrerss:0xf6c3d0 里面元素的地址一样
mutA 与 mutB 当改变mutB 里面元素的name值,mutA 里面的值改变不

因为 mutA 与 mutB 里面的元素地址是一样的,所以改变 mutB 里面的值, mutA 也会被改变。
mutA 与 mutB 当改变mutB 里面元素的name值时,mutA 的值不变
copyItems: 等于YES

NSMutableArray *mutA = [NSMutableArray arrayWithObjects:a,b, nil]; NSMutableArray *mutB = [[NSMutableArray alloc] initWithArray:mutA copyItems:YES];
// Person - (id)copyWithZone:(NSZone *)zone { Person *person = [[Person allocWithZone:zone] init]; person.name = self.name; return person; }
copyItems: 等于YES 系统会向集合中的每一个元素对象发送一个copyWithZone:消息 而在copyWithZone: 的时候我创建了一个新对象,
数组的归档解挡
#import "Person.h" @implementation Person - (instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { self.name = [aDecoder decodeObjectForKey:@"name"]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.name forKey:@"name"]; } @end

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/226200.html原文链接:https://javaforall.net
