iOS浅拷贝、深拷贝和完全深拷贝

1、前言
最近开发中经常遇到这个问题,不过发现网上能完整的总结的还是很少。今天就动手把这个以我的角度总结一下。尽量把问题说明白:
2、概念一
浅拷贝:指针拷贝,两个指针指向同一块内存地址。
深拷贝:内存块拷贝,两个内存地址完全独立或者说完全不同。
2.1、关于copy和mutableCopy
顾名思义,copy就是复制了一个不可变的对象,而mutablecopy就是复制了一个可变的对象。 一个NSObject的对象要想使用这两个函数,那么类必须实现NSCopying协议和NSMutableCopying协议。 对于NSCopying,实现+ copyWithZone:方法。 对于NSMutableCopying,实现+ mutableCopyWithZone:方法。 当然这种情况一般是在自定义结构体时会用,这个我会在后面举例说一下。但经常用的NSString,NSArray,NSDictionary等系统提供的结构体都已实现。
2.2、举个例子
NSString、NSMutableString、NSArray、NSMutableArray分别进行copy和mutableCopy时的情况 上面所说的四种结构体可以分为两种类型。
2.2.1、系统的非容器类对象 这里指的是NSString,NSNumber等等一类的对象。
2.2.2、系统的容器类对象 指NSArray,NSDictionary等。
下面我对对这4种结构体分别进行举例,分为8总情况。我会配上相应的内存截图,以方便阅读。
【iOS浅拷贝、深拷贝和完全深拷贝】例子1、对于一个不可变的对象(NSString)
NSString * string = @"weisheng.wang";
NSString * stringCopy = [string copy];
NSString * stringMutableCopy = [string mutableCopy];
NSLog(@"string:%p",string);
NSLog(@"stringCopy:%p",stringCopy);
NSLog(@"stringMutableCopy:%p",stringMutableCopy);
TodayProject[1347:30830] string:0x712c
TodayProject[1347:30830] stringCopy:0x712c
TodayProject[1347:30830] stringMutableCopy:0x79e8d140
例子2、对于一个可变的对象(NSMutableString)
NSMutableString * string =[NSMutableString stringWithFormat:@"weisheng.wang"];
NSString * stringCopy = [string copy];
NSMutableString * stringMutableCopy = [string mutableCopy];
NSLog(@"string:%p",string);
NSLog(@"stringCopy:%p",stringCopy);
NSLog(@"stringMutableCopy:%p",stringMutableCopy);
TodayProject[1508:35092] string:0x7a7890d0
TodayProject[1508:35092] stringCopy:0x7a783400
TodayProject[1508:35092] stringMutableCopy:0x7a786ef0
2.2.3、例子1、例子2总结:
对于系统的非容器类对象(NSString,NSNumber)
如果对一不可变对象进行复制,copy是指针复制(浅拷贝)而mutableCopy就是内存块复制(深拷贝)。
如果是对一可变对象复制,都是深拷贝,但是copy返回的对象是不可变的。
例子3、对NSArray进行copy和mutableCopy操作
NSArray * originArray = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
NSArray * originArrayCopy = [originArray copy];
NSMutableArray * originArrayMutableCopy = [originArray mutableCopy];
[originArrayMutableCopy addObject:@"d"];
[originArrayMutableCopy removeObjectAtIndex:0];


1、内存截图: iOS浅拷贝、深拷贝和完全深拷贝
文章图片
2de5d8c5dcd37f504b9a6d3088244bfb.png
2、添加一个元素后: iOS浅拷贝、深拷贝和完全深拷贝
文章图片
添加一个元素后.png 3、删除一个元素后 iOS浅拷贝、深拷贝和完全深拷贝
文章图片
171a3558c9c695556288677e4badad98.png
2.2.4、例子3总结:
originArray和originArrayCopy是浅复制,而originArrayMutableCopy是深拷贝,originArrayMutableCopy还可以改变数组内的元素:删除或添加。但是注意的是,容器内的元素内容都是浅拷贝。
例子4、修改元素的值
NSArray * originArray = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"], @"b", @"c", nil];
NSArray * originArrayCopyArray = [originArray copy];
NSMutableArray * originArrayMutableCopyArray = [originArray mutableCopy];
// originArrayMutableCopyArray,originArrayCopyArray和originArray其中的元素都是一样的对象——同一个指针
//注意:这里只能是将值先取出来通过append来修改,如果用testString = @"d"; 这样会改变testString的指针,其实是将@“d”临时对象赋给了testString
NSMutableString * testString = [originArray objectAtIndex:0];
[testString appendString:@" changevalue"]; // 这样以上三个数组的首元素都被改变了
内存截图


1、修改值之前 iOS浅拷贝、深拷贝和完全深拷贝
文章图片
f7fdf23fbd165cda4d17b49ba61fdaa4.png 2、修改值之后 iOS浅拷贝、深拷贝和完全深拷贝
文章图片
43803eb224bdb298d02f3c955be4b93d.png
3、用testString = @"d"修改值后内存情况,这样以数组中的元素没有任何影响,当然也没有任何意义 iOS浅拷贝、深拷贝和完全深拷贝
文章图片
6f2baa450093b3b862dba6dd616d9d64.png 例子5:对NSMutableArray进行copy和mutableCopy操作
NSMutableArray *originArray = [NSMutableArray arrayWithObjects:@"a",@"b",@"c", nil];
NSArray *originArrayCopy = [originArray copy];
NSMutableArray *originArrayCopy1 = [originArray copy];
[originArrayCopy1 addObject:@"d"]; //mutable对象copy之后成为imutable对象,所以添加元素会error
NSMutableArray *originArrayMutableCopy = [originArray mutableCopy];


内存截图 iOS浅拷贝、深拷贝和完全深拷贝
文章图片
7e9f01616e6ef74bc79124e0edfb5611.png 2.2.4、例子4、例子5总结:
1.对于容器类本身,与非容器类对象的结论相同,即
如果对一不可变对象进行复制,copy是指针复制(浅拷贝)而mutableCopy就是对象复制(深拷贝)。
如果是对可变对象复制,都是深拷贝,但是copy返回的对象是不可变的。
关键要注意的是复制后容器内对象的变化,比如添加、删除元素,修改某个元素的值。
2.对于容器而言,其元素对象始终是指针拷贝(浅拷贝)。如果需要元素对象也是深拷贝,就需要实现完全深拷贝。
3、概念二
完全深拷贝:指的是容器本身的地址和容器内元素的地址 不一样
例子6、完全深拷贝的一种实现
NSArray * array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],[NSString stringWithString:@"b"],@"c",nil];
NSArray * deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array]];


内存截图: iOS浅拷贝、深拷贝和完全深拷贝
文章图片
51d3e7bd2d0c2378fcfa71b62d035699.png 3.1 例子6总结
1.trueDeepCopyArray是完全意义上的深拷贝,而deepCopyArray则不是,对于deepCopyArray内的元素还是浅拷贝
2.[deepCopyArray objectAtIndex:0]因为原来是可变对象,还和上面的结论一样,依旧是深拷贝。
3.用归档的方法实现了真正的元素深拷贝。

    推荐阅读