实例化的对象存储在堆区,而指向对象的指针一般存储在栈区。实际上拷贝分为深拷贝(one level deep copy),浅拷贝(shallow copy)和完全拷贝(real deep copy)三种。
-
浅拷贝:在操作中,对于被复制对象的每一层都是指针复制。
-
深拷贝:在操作中,对于被复制对象,至少有一层是深复制。
- 完全拷贝:在操作中,对于被复制对象,每一层都是对象复制。
注意
1、深拷贝和完全拷贝
深拷贝,就是把原有对象内容直接克隆一份新的对象,但是这里有一个坑,就是深拷贝只是复制一层对象,而不是复制第二层或者更深层的对象,例如:
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *strM1 = [NSMutableString stringWithString:@"1"];
NSMutableString *strM2 = [NSMutableString stringWithString:@"2"];
NSMutableArray *arrM1 = [NSMutableArray arrayWithObjects: strM1, strM2, nil];
// 深拷贝原数组
NSMutableArray *arrM2 = [arrM1 mutableCopy];
// 深拷贝之后,再修改字符“1“ 为 ”11“
NSMutableString *strM3 = [arrM1 objectAtIndex:0];
[strM3 appendString:@"1"];
NSLog(@"strM1--%@",arrM1);
NSLog(@"strM2--%@",arrM2);
}
看结果:
// 原数组
2017-03-14 00:22:02.260 [5522:393850] strM1--(
11,
2
)
// 深拷贝数组
2017-03-14 00:22:02.261 [5522:393850] strM2--(
11,
2
)
结论:为什么深拷贝已经复制了对象,那么原对象为什么也同步变化?这里就是深拷贝和完全拷贝的原因,深拷贝只是拷贝了一层数组,但是里面的字符串没有拷贝,两个数组都是用的同一个地址的字符串,所以改变一个,原对象也发生了变化。可以做下面这样的修改。
// copyIitems为YES
NSMutableArray *arrM2 = [[NSMutableArray alloc] initWithArray: arrM1 copyItems:YES];
查看结果
2017-03-14 00:45:45.389 [5916:425450] arrM1--(
11,
2
)
2017-03-14 00:45:45.390 [5916:425450] arrM2--(
1,
2
)
可以利用这个方法,得到的是多一层的深复制,里面的字符串地址也进行了复制,所以改变strM3的值,不影响arrM1的值。你认为这样就解决了吗?再看下面的问题。
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *strM1 = [NSMutableString stringWithString:@"1"];
NSMutableString *strM2 = [NSMutableString stringWithString:@"2"];
NSMutableArray *arrM1 = [NSMutableArray arrayWithObjects: strM1, strM2, nil];
// 嵌套了一层数组,arrM1
NSMutableArray *arrM2 = [NSMutableArray arrayWithObjects: strM1, strM2, arrM1, nil];
// copyItems会多拷贝一层
NSMutableArray *arrM3 = [[NSMutableArray alloc] initWithArray:arrM2 copyItems:YES];
// 两层深拷贝后再修改字符串
NSMutableString *strM3 = [arrM1 objectAtIndex:0];
[strM3 appendString:@"1"];
NSLog(@"arrM2--%@",arrM2);
NSLog(@"arrM3--%@",arrM3);
}
查看结果
2017-03-14 00:55:57.604 [6080:438490] arrM2--(
11,
2,
(
11,
2
)
)
2017-03-14 00:55:57.606 [6080:438490] arrM3--(
// 第二层没有变化
1,
2,
(
// 第三层的字符串同步变化了。因为copyItem只是多拷贝了一层
11,
2
)
)
结论:可以发现外层的深复制了,原对象和拷贝后的对象不是同一地址,再往里看一层都变化了,就没有深复制,也就是说再增加一层,NSMutableArray *arrM2 = [[NSMutableArray alloc] initWithArray:arrM1 copyItems:YES];
这个方法管不了那么多层数。可以采用归档和解档解决该问题:
NSMutableArray *arrM3 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:arrM2]];
查看结果
2017-03-14 01:17:23.204 [6396:464958] arrM2--(
11,
2,
(
11,
2
)
)
2017-03-14 01:17:23.204 [6396:464958] arrM3--(
1,
2,
(
1,
2
)
)
结论:可以看到实现了完全复制,就没有层数的限制。
2、NSString的copy和strong
NSString被copy和strong修饰会有什么不同:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic,copy) NSString *str;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *strM = [[NSMutableString alloc] initWithString:@"bestDay"];
self.str = strM;
[strM appendString:@"OfThisYear"];
NSLog(@"str----%@---%p",self.str,self.str);
NSLog(@"strM----%@---%p",strM,strM);
}
@end
直接查看结果
2017-03-14 08:53:01.874 [789:18128] str----bestDay---0xa796144747365627
2017-03-14 08:53:01.875 [789:18128] strM----bestDayOfThisYear---0x600000071280
结论:可以看到copy修饰的str,在赋值以后,可变字符串strM发生了变化并不会影响str的值。从打印结果来看是因为二者不是一个地址,所以不会相互影响。为什么?
是因为copy修饰的属性setter方法,先release旧值,copy新值再赋值给成员变量,不可变copy是深拷贝,就是内容拷贝,地址变化了
接着看strong修饰的情况:
@property (nonatomic,strong) NSString *str;
直接查看结果
2017-03-14 09:03:36.756 [968:29890] str----bestDayOfThisYear---0x6000002600c0
2017-03-14 09:03:36.757 [968:29890] strM----bestDayOfThisYear---0x6000002600c0
结论:被strong修饰以后只是强指针引用,并未改变地址,所以str的值会随着strM进行变化,二者的地址也是相同的。
3、理解copy和retain的不同
在MRC下进行测试,先看代码
//copy和retain的区别
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *arrM = [NSMutableArray arrayWithObjects:@"111",@"222",@"333", nil];
NSMutableArray *arrMRetain = [arrM retain];
NSMutableArray *arrMCopy = [arrM copy];
[arrM removeLastObject];
NSLog(@"arrMRetain--%@--%p--%lu",arrMRetain,arrMRetain,[arrMRetain retainCount]);
NSLog(@"arrMCopy--%@--%p--%lu",arrMCopy,arrMCopy,[arrMCopy retainCount]);
}
查看结
2017-03-14 20:16:59.895 copy和retain的区别[2816:177901] arrMRetain--(
111,
222
)--0x60000005cf50--2
2017-03-14 20:16:59.895 [2816:177901] arrMCopy--(
111,
222,
333
)--0x60000005cf80--1
结论:copy是深复制,retainCount为1,retain是浅复制,retain是使原来对象引用计数加1,所以arrM和arrMRetain是同一地址,所以remove最后一个元素,arrMRetain也跟着变化了。
参考
ios中的拷贝你知道多少?