前言
关于拷贝,小到面试题,大到找不出 Bugs。
在 iOS 中分为 浅拷贝 和 深拷贝,对应 Foundation 中的基本数据类型,系统已经实现和遵循了 NSCopying 协议和方法。下面大家来了解一下。
深拷贝与浅拷贝
1 2 3 4 5 6 7 8 9 10 11
| @protocol NSCopying
- (id)copyWithZone:(nullable NSZone *)zone;
@end
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(nullable NSZone *)zone;
@end
|
区别:
浅拷贝:指针拷贝,共用一个内存地址。
深拷贝:新的指针,新的内存地址。
不可变对象进行 copy, 不会生成新的对象(类型:eg:NSString),共用一个地址。
可变对对象进行 copy, 会生成新的对象(类型:eg:NSMutableString),新的内存地址。
1 2 3 4 5 6
| NSString * oldStr = @"older"; NSString * newStr = [oldStr copy]; NSLog(@"%p - %p - %@", oldStr, newStr, [oldStr class]); oldStr = [oldStr stringByAppendingString:@"12"];//生成新的对象,并指向新的指针地址 NSLog(@"%@ - %@ - %@", oldStr, newStr, [newStr class]); NSLog(@"%p - %p", oldStr, newStr);
|
1 2 3
| 0x1021e40d8 - 0x1021e40d8 - __NSCFConstantString older12 - older - __NSCFConstantString 0x600002229bf0 - 0x1021e40d8
|
1 2 3 4 5 6
| NSMutableString * oldMStr = [NSMutableString stringWithString:@"mutableStr"]; NSMutableArray * newMStr = [oldMStr copy]; NSLog(@"%p - %p - %@", oldMStr, newMStr, [oldMStr class]); [oldMStr appendString:@"-M"];//指针地址不变 NSLog(@"%@ - %@ - %@", oldMStr, newMStr, [newMStr class]); NSLog(@"%p - %p", oldMStr, newMStr);
|
1 2 3
| 0x6000022297a0 - 0x600002c422e0 - __NSCFString mutableStr-M - mutableStr - __NSCFString 0x6000022297a0 - 0x600002c422e0
|
不可变对象进行 mutableCopy, 会生成新的对象(类型:eg:NSMutableString),新的内存地址。
可变对象进行 mutableCopy, 会生成新的对象(类型:eg:NSMutableString),新的内存地址。
1 2 3 4 5 6
| NSArray * oldArr = [NSArray arrayWithObject:@"array"]; NSArray * newArr = [oldArr mutableCopy]; NSLog(@"%p - %p - %@", oldArr, oldArr, [oldArr class]); oldArr = [oldArr arrayByAddingObject:@"-arr"];//新的对象,新的内存地址 NSLog(@"%ld - %ld - %@", oldArr.count, newArr.count, [newArr class]); NSLog(@"%p - %p", oldArr, newArr);
|
1 2 3
| 0x600002e73c50 - 0x600002e73c50 - __NSSingleObjectArrayI 2 - 1 - __NSArrayM 0x600002c42260 - 0x600002233d80
|
1 2 3 4 5 6
| NSMutableArray * oldMArr = [NSMutableArray arrayWithObject:@"mutableArray"]; NSMutableArray * newMArr = [oldMArr mutableCopy]; NSLog(@"%p - %p - %@", oldMArr, newMArr, [oldMArr class]); [oldMArr addObject:@"-arr"];//内存地址不变 NSLog(@"%ld - %ld - %@", oldMArr.count, newMArr.count, [newMArr class]); NSLog(@"%p - %p", oldMArr, newMArr);
|
1 2 3
| 0x600002229830 - 0x600002229e30 - __NSArrayM 2 - 1 - __NSArrayM 0x600002229830 - 0x600002229e30
|
对于自定义对象,若要使用 copy 或 mutableCopy 需要遵循协议,实现协议方法。
知识点
Q: 在 property 中,为什么常用 copy 修饰 string 字符串,而不用 strong 修饰?
分析:首先 Strong 会指向内存地址,如果用 strong 修饰可变字符时,当指向的内存地址发生变化时,属性中的值也会相应变化;copy 修饰,相当于对象进行浅拷贝(self.obja = [objb copy])。
1 2
| @property (nonatomic, copy) NSString * string0; @property (nonatomic, strong) NSString * string1;
|
对应不可变的字符串来说,相当于浅拷贝,指针拷贝。
1 2 3 4 5
| NSString * string0 = @"string0"; self.string0 = string0; self.string1 = string0; string0 = @"0";//指针地址发生变化 NSLog(@"%p, %p, %p", self.string0, self.string1, string0);
|
1
| 0x10c59c098, 0x10c59c098, 0x10c59c0b8
|
对应可变的字符串来说,copy 修饰相当于深拷贝,strong 修饰为指针拷贝。
1 2
| @property (nonatomic, copy) NSMutableString * mString0; @property (nonatomic, strong) NSMutableString * mString1;
|
1 2 3 4 5
| NSMutableString * pMstr = [NSMutableString stringWithString:@"string"]; self.mString0 = pMstr; self.mString1 = pMstr; [pMstr appendString:@"- cs"]; NSLog(@"%p, %p, %p", self.mString0, self.mString1, pMstr);
|
1
| 0xdd890020b715b501, 0x600000ba9da0, 0x600000ba9da0
|
A:对应不可变的字符串来说,相当于浅拷贝,指针拷贝;对应可变的字符串来说,copy 修饰相当于深拷贝,strong 修饰为指针拷贝。若不想属性成员变量随着变化,请用 copy 修饰。
参考资料
文档信息