一:概念
浅拷贝:指针拷贝,不会创建一个新的对象。浅拷贝简单点说就是对内存地址的复制,让目标对象指针和源对象指针指向同一片内存空间
深拷贝: 内容拷贝,会创建一个新的对象。深拷贝就是拷贝地址中的内容,让目标对象产生新的内存区域,且将源内存区域中的内容复制到目标内存区域中
深拷贝和浅拷贝的本质是内存地址是否相同
二:各种类型的对象深拷贝,浅拷贝
1.非容器类对象(比如像NSString,NSNumber这些不能包含其他对象的叫做非容器类,如NSArray和NSDictionary可以容纳其他对象的叫做容器类对象)
- (void)TestCopy{
//不可变对象
NSString *str = @"zww";
NSString *str1 = [str copy]; //浅拷贝
NSString *str2 = [str mutableCopy]; //深拷贝
NSMutableString *str3 = [str copy]; //浅拷贝
//crash
// [str3 appendString:@"333"]; //对 NSString copy结果为不可变对象
NSMutableString *str4 = [str mutableCopy]; //深拷贝
//正常
[str4 appendString:@"333"]; //对 NSString mutableCopy结果为可变对象
ZWWLog(@"指针值str=%p,str1=%p",str,str1);
ZWWLog(@"指针值str=%p,str2=%p",str,str2);
ZWWLog(@"指针值str=%p,mStr1=%p",str,str3);
ZWWLog(@"指针值str=%p,mStr2=%p",str,str4);
NSLog(@"str4==%@",str4);
//可变对象
NSMutableString *mStr = [NSMutableString stringWithFormat:@"coder"];
NSString *mStr1 = [mStr copy]; //深拷贝
NSString *mStr2 = [mStr mutableCopy]; //深拷贝
NSMutableString *mStr3 = [mStr copy]; //深拷贝
// [mStr3 appendString:@"333"]; //crash //对 NSMutableString copy结果为不可变对象
NSMutableString *mStr4 = [mStr mutableCopy]; //深拷贝
[mStr4 appendString:@"444"]; //对 NSMutableString mutableCopy结果为可变对象
ZWWLog(@"指针值mStr=%p,mStr1=%p",mStr,mStr1);
ZWWLog(@"指针值mStr=%p,mStr2=%p",mStr,mStr2);
ZWWLog(@"指针值mStr=%p,mStr3=%p",mStr,mStr3);
ZWWLog(@"指针值mStr=%p,mStr4=%p",mStr,mStr4);
NSLog(@"mStr4==%@",mStr4 );
}
打印结果:
2018-08-01 16:32:48.847096+0800 InterviewDemo[43235:359669] -[ZWWTableViewController(method) TestCopy] [Line 104] 指针值str=0x1054b53f0,str1=0x1054b53f0
2018-08-01 16:32:48.847236+0800 InterviewDemo[43235:359669] -[ZWWTableViewController(method) TestCopy] [Line 105] 指针值str=0x1054b53f0,str2=0x60c000250fb0
2018-08-01 16:32:48.847621+0800 InterviewDemo[43235:359669] -[ZWWTableViewController(method) TestCopy] [Line 106] 指针值str=0x1054b53f0,mStr1=0x1054b53f0
2018-08-01 16:32:48.847946+0800 InterviewDemo[43235:359669] -[ZWWTableViewController(method) TestCopy] [Line 107] 指针值str=0x1054b53f0,mStr2=0x60c0002512e0
2018-08-01 16:32:48.848180+0800 InterviewDemo[43235:359669] str4==zww333
2018-08-01 16:32:48.848356+0800 InterviewDemo[43235:359669] -[ZWWTableViewController(method) TestCopy] [Line 119] 指针值mStr=0x60400005ede0,mStr1=0xa00007265646f635
2018-08-01 16:32:48.848693+0800 InterviewDemo[43235:359669] -[ZWWTableViewController(method) TestCopy] [Line 120] 指针值mStr=0x60400005ede0,mStr2=0x60400005eed0
2018-08-01 16:32:48.848760+0800 InterviewDemo[43235:359669] -[ZWWTableViewController(method) TestCopy] [Line 121] 指针值mStr=0x60400005ede0,mStr3=0xa00007265646f635
2018-08-01 16:32:48.849003+0800 InterviewDemo[43235:359669] -[ZWWTableViewController(method) TestCopy] [Line 122] 指针值mStr=0x60400005ede0,mStr4=0x60400005edb0
2018-08-01 16:32:48.849102+0800 InterviewDemo[43235:359669] mStr4==coder444
结论:
- 无论是对NSString类型还是对NSMutableString进行mutableCopy,得到的内存地址都与原对象地址不同,mutableCopy是真正意义上的深拷贝
- NSString,NSMutableString和copy,mutableCopy4种组合方式只有对不可变对象NSString类型进行copy后得到的内存地址和原对象地址相同,是浅拷贝,其他都是深拷贝
copy得到的对象都仍然是不可变对象,mutableCopy的到的对象仍然是不可变对象
这里可以回答一个经常面试问到问题:
对NSMutableString用copy修饰会有什么问题?
答:对NSMutableString用copy修饰得到的是不可变类型NSString对象,如果再对这个对象进行改变会crash
2.自定义对象的深拷贝,浅拷贝
自定义对象的深拷贝,浅拷贝就没有什么规则可言了,就完全看怎么实现对应的NSCopying或者NSMutableCopying协议了
自定义一个Model1类,并声明一个NSString类型的name属性
Model1.h
#import <Foundation/Foundation.h>
@interface Model1 : NSObject<NSCopying>
@property (nonatomic, copy) NSString *name;
@end
Model1.m
//浅拷贝(指针复制,不会创建一个新的对象)
- (id)copyWithZone:(NSZone *)zone{
return self;
}
//深拷贝(内容复制,会创建一个新的对象)
//- (id)copyWithZone:(NSZone *)zone{
// //创建新的对象空间
// Model1 *model = [[Model1 allocWithZone:zone]init];
// //属性也进行深层拷贝
// model.name = [self.name mutableCopy];
// return model;
//}
调用:
- (void)TestCopy1{
Model1 *model = [[Model1 alloc]init];
model.name = @"coder";
Model1 *copyModel = [model copy];
ZWWLog(@"指针值model==%p,copyModel==%p",model,copyModel);
}
实现浅拷贝协议代码时打印结果:
指针值model==0x6080000119e0,copyModel==0x6080000119e0
属性地址model.name==0x1048644d0,copyModel.name==0x1048644d0
分析:可以看到指针地址相同,属性地址也相同,是浅拷贝
实现深拷贝协议代码时打印结果:
指针值model==0x608000035900,copyModel==0x608000036c80
属性地址model.name==0x10d1d44d0,copyModel.name==0xa00007265646f635
分析:可以看到指针地址不同,属性地址也不同,是深拷贝
3.容器类对象的深拷贝,浅拷贝
- (void)TestCopy201{
// 和NSString浅copy的验证步骤一样
NSArray *arr = [NSArray arrayWithObjects:@"1", nil];
NSArray *copyArr = [arr copy];
ZWWLog(@"******************浅copy******************");
ZWWLog(@"原对象指针arr==%p,copyArr==%p", arr,copyArr);
ZWWLog(@"******************单层深copy******************");
NSArray *mCopyArr = [arr mutableCopy];
ZWWLog(@"原对象指针arr==%p,mCopyArr==%p", arr,mCopyArr);
ZWWLog(@"指针变量地址==%p, arr对象地址==%p", &arr,arr);
ZWWLog(@"指针变量地址==%p, mCopyArr对象地址==%p",©Arr, mCopyArr);
//打印arr、copyArr内部元素进行对比
ZWWLog(@"arr对象首元素地址==%p", arr[0]);
ZWWLog(@"mCopyArr对象首元素地址==%p", mCopyArr[0]);
ZWWLog(@"******************双层深copy******************");
[self TestCopy203];
}
对NSArray copy打印结果:
原对象指针arr==0x60c000031be0,copyArr==0x60c000031be0
可以看到:指针相同,指向的是同一块内存,所以对NSArray进行copy是浅拷贝
你可能会问,为什么不看一下arr和copyArr内部元素的指针对比?这里并没有必要,最外层对象都没有创建新的,里面不用验证
对NSArray mutableCopy打印结果:
原对象指针arr==0x60000002d360,mCopyArr==0x60c0000572b0
指针变量地址==0x7ffee2444bc8, arr对象地址==0x60000002d360
指针变量地址==0x7ffee2444bc0, mCopyArr对象地址==0x60c0000572b0
arr对象首元素地址==0x10d7c35d0
mCopyArr对象首元素地址==0x10d7c35d0
分析:
- arr和mCopyArr地址不同,实现了外层NSArray对象的深拷贝
- 但是arr首元素和mCopyArr首元素地址相同,证明并未对其容器内对象进行处理
- 单层深拷贝
容器的双层深copy:这里的双层指的是完成了NSArray对象和NSArray容器内对象的深copy(为什么不说完全,是因为无法处理NSArray中还有一个NSArray这种情况)。
- (void)TestCopy203{
// 随意创建一个NSMutableString对象
NSMutableString *mStr = [NSMutableString stringWithString:@"zww"];
// 随意创建一个包涵NSMutableString的NSMutableArray对象
NSMutableString *mStr1 = [NSMutableString stringWithString:@"111"];
NSMutableArray *mArr = [NSMutableArray arrayWithObjects:mStr1, nil];
// 将mutableString和mutableArr放入一个新的NSArray中
NSArray *testArr = [NSArray arrayWithObjects:mStr, mArr, nil];
// 通过官方文档提供的方式创建
NSArray *testArrCopy = [[NSArray alloc] initWithArray:testArr copyItems:YES];
// testArr和testArrCopy指针对比
ZWWLog(@"原对象指针testArr==%p,testArrCopy==%p", testArr,testArrCopy);
// testArr和testArrCopy中元素指针对比
// mStr对比
ZWWLog(@"首元素mStr地址testArr[0]==%p,testArrCopy[0]==%p", testArr[0],testArrCopy[0]);
// mArr对比
ZWWLog(@"其中元素mArr地址testArr[1]==%p,testArrCopy[1]==%p", testArr[1],testArrCopy[1]);
// mArr中的元素对比,即mStr1对比
ZWWLog(@"其中元素mArr里面包含元素mStr1的地址testArr[1][0]==%p,testArrCopy[1][0]==%p", testArr[1][0],testArrCopy[1][0]);
}
打印结果:
原对象指针testArr==0x60c000234160,testArrCopy==0x60c000235580
首元素mStr地址testArr[0]==0x60c00024a230,testArrCopy[0]==0xa0000000077777a3
其中元素mArr地址testArr[1]==0x60c0002496c0,testArrCopy[1]==0x60c00001e410
其中元素mArr里面包含元素mStr1的地址testArr[1][0]==0x60c0002498a0,testArrCopy[1][0]==0x60c0002498a0
分析:
可以看到testArr和testArrCopy地址不同,实现了Arr本身的深拷贝,首元素mStr地址和第二个元素testArr地址也不同,实现了双层深拷贝,但是数组里面包含的数组元素里面的元素地址还是相同的,所以说并没有实现“完全”深拷贝