iOS深拷贝和浅拷贝

一:概念
浅拷贝:指针拷贝,不会创建一个新的对象。浅拷贝简单点说就是对内存地址的复制,让目标对象指针和源对象指针指向同一片内存空间
深拷贝: 内容拷贝,会创建一个新的对象。深拷贝就是拷贝地址中的内容,让目标对象产生新的内存区域,且将源内存区域中的内容复制到目标内存区域中
深拷贝和浅拷贝的本质是内存地址是否相同

二:各种类型的对象深拷贝,浅拷贝
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",&copyArr, 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地址也不同,实现了双层深拷贝,但是数组里面包含的数组元素里面的元素地址还是相同的,所以说并没有实现“完全”深拷贝

猜你喜欢

转载自blog.csdn.net/wei371522/article/details/81333859