浅拷贝与深拷贝

浅拷贝与深拷贝

程序中经常会遇到集合类的传值

坑: 数组操作时对于数组中的对象拷贝

###目的:
观察array1、mArrayCopy、mArrayCopy2 三者区别

1
2
3
NSArray *array1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *mArrayCopy = [[NSArray alloc] initWithArray:array1 copyItems:YES];
NSArray* mArrayCopy2 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array1]];

首先了解copy与retain的区别

OC对象引用计数器:屋里开灯规则

copy是创建一个新对象,retain是创建一个指针,引用对象计数加1。Copy属性表示两个对象内容相同,新的对象retain为1 ,与旧有对象的引用计数无关,旧有对象没有变化。

retain属性表示两个对象地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1也就是说,retain 是指针拷贝,copy 是内容拷贝。

主角:copy、mutableCopy

注意:可变和不可变对象使用copy、mutableCopy的区别

遵守NSCopying 协议的类可以发送copy消息,
遵守NSMutableCopying 协议的类才可以发送mutableCopy消息。

默认的ios类并没有遵守这两个协议
自定义copy 必须遵守NSCopying,并且实现 copyWithZone: 方法
自定义mutableCopy 那么就必须遵守NSMutableCopying,并且实现 mutableCopyWithZone: 方法

实例

系统的非容器类对象

这里指的是NSString,NSNumber等等一类的对象。

示例1:
不可变string

1
2
3
4
5
6
7
8
9
10
NSString *string = @"origionStr";
NSString *string2 = string;
NSString *stringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
[stringMCopy appendString:@"!!!!"];
// log
NSLog(@"string = %p %p %@ ",string,&string,string);
NSLog(@"string2 = %p %p %@ ",string2,&string2,string2);
NSLog(@"stringCopy = %p %p %@ ",stringCopy,&stringCopy,stringCopy);
NSLog(@"stringMCopy = %p %p %@ ",stringMCopy,&stringMCopy,stringMCopy);

结果:

1
2
3
4
string = 0x1064e7088 0x7fff597190a0 origionStr
string2 = 0x1064e7088 0x7fff59719098 origionStr
stringCopy = 0x1064e7088 0x7fff59719090 origionStr
stringMCopy = 0x7fafebdbb4b0 0x7fff59719088 origionStr!!!!

示例2:
可变string

1
2
3
4
5
6
7
8
9
10
NSMutableString *string = [NSMutableString stringWithString: @"origion"];
NSString *stringCopy = [string copy];
NSMutableString *mStringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
[string appendString:@" origion!"];
[stringMCopy appendString:@"!!"];
NSLog(@"string = %p %@",string,string);
NSLog(@"stringCopy = %p %@",stringCopy,stringCopy);
NSLog(@"stringMCopy = %p %@",mStringCopy,mStringCopy);
NSLog(@"stringMCopy = %p %@",stringMCopy,stringMCopy);

结果:

1
2
3
4
string = 0x7fafebdec820 origion origion!
stringCopy = 0x7fafebdcb8c0 origion
stringMCopy = 0x7fafebda4320 origion
stringMCopy = 0x7fafebd32890 origion!!

对于系统的非容器类对象,我们可以认为,如果对一不可变对象复制,copy是指针复制(浅拷贝)和mutableCopy就是对象复制(深拷贝)。如果是对可变对象复制,都是深拷贝,但是copy返回的对象是不可变的。

系统的容器类对象

指NSArray,NSSet,NSDictionary等。对于容器类本身,上面讨论的结论也是适用的,需要探讨的是复制后容器内对象的变化。

示例1:
不可变数组

1
2
3
4
5
6
7
8
9
10
//copy返回不可变对象,mutablecopy返回可变对象
NSArray *array1 = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
NSArray *arrayCopy1 = [array1 copy];
NSMutableArray *mArrayCopy1 = [array1 mutableCopy];
[mArrayCopy1 addObject:@"de"];
[mArrayCopy1 removeObjectAtIndex:0];
// log
NSLog(@"array1 = %p %@ %p %@",array1,array1,array1[1],array1[1]);
NSLog(@"arrayCopy1 = %p %@ %p %@",arrayCopy1,arrayCopy1,arrayCopy1[1],arrayCopy1[1]);
NSLog(@"mArrayCopy1 = %p %@ %p %@",mArrayCopy1,mArrayCopy1,mArrayCopy1[0],mArrayCopy1[0]);

结果:

1
2
3
array1 = 0x7fafebdeaed0 (a,b,c) 0x1064e7228 b
arrayCopy1 = 0x7fafebdeaed0 (a,b,c) 0x1064e7228 b
mArrayCopy1 = 0x7fafebdfc010 (b,c,de) 0x1064e7228 b

示例:
可变数组

1
2
3
4
NSArray *array1 = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
NSMutableArray *mArrayCopy = [[NSMutableArray alloc] initWithArray:array1 copyItems:YES];
NSLog(@"array1 = %p %@ %p %@ %p %@",array1, array1, array1[0], array1[0], array1[1], array1[1]);
NSLog(@"arrayCopy1 = %p %@ %p %@ %p %@",mArrayCopy,mArrayCopy, mArrayCopy[0], mArrayCopy[0], mArrayCopy[1], mArrayCopy[1]);

结果:

1
2
array1 = 0x7fafebcb5ae0 (a,b,c) 0x10afe1208 a 0x10afe1228 b
arrayCopy1 = 0x7fafebcb5b10 (a,b,c) 0x10afe1208 a 0x10afe1228 b

示例2:

1
2
3
NSArray *array1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSMutableArray *mArrayCopy = [[NSMutableArray alloc] initWithArray:array1 copyItems:YES];
NSArray* mArrayCopy2 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array1]];

mArrayCopy2是完全意义上的深拷贝,而mArrayCopy则不是

对于mArrayCopy内的不可变元素其还是指针复制。或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。