原文发布于:wenghengcong.com
一.基本类型的拷贝
一般来讲,标准的复制,指的是简单的赋值操作的调用,也就是使用 = 操作符来赋值一个变量给另一个变量,比如说:
int a = 5;
int b;
b = a;
那么b就获得了一份a的拷贝,b和a的内存地址是不同的,他们各占不同的内存区域。但是如果你这种方式企图复制一个Core Foundation对象,那么复制的仅仅是对象的引用,而对象本身并没有得到实际的复制。
不管是容器类对象还是非容器类对象,copy返回不可变对象,mutablecopy返回可变对象。
二.系统的非容器类对象
下面以NSString
和NSMutableString
说明非容器类对象的拷贝:
- 不可变对象的copy
NSString *str = [NSString stringWithFormat:@"123"];
NSString *c= [str copy];//浅拷贝,str:123 ,cpstr:123
NSLog(@"STR:%p recount = %ld",str,[str retainCount]);
NSLog(@"CPstr:%p recount = %ld",cpstr,[cpstr retainCount]);
2015-08-06 15:37:42.001 TableViewDemo[57923:1652639] STR:0x7fe88272d0f0 recount = 2
2015-08-06 15:37:44.760 TableViewDemo[57923:1652639] CPstr:0x7fe88272d0f0 recount = 2
str与cpstr指向同一个对象,copy后对象的引用计数增加为2
- 不可变对象的mutableCopy
NSString *str = [NSString stringWithFormat:@"123"];
NSMutableString *cpstr = [str mutableCopy];//深拷贝,str:1 ,cpstr:15
NSLog(@"STR:%p recount = %ld",str,[str retainCount]);
NSLog(@"CPstr:%p recount = %ld",cpstr,[cpstr retainCount]);
2015-08-06 15:44:07.437 TableViewDemo[58020:1656801] STR:0x7f8610723090 recount = 1
2015-08-06 15:44:07.437 TableViewDemo[58020:1656801] CPstr:0x7f8610721010 recount = 1
str与cpstr指向不同的对象,mutableCopy没有影响str的引用计数。
- 可变对象的copy
NSMutableString *str = [NSMutableString stringWithFormat:@"123"];
NSString *cpstr = [str copy];//深拷贝,str:123 ,cpstr:123
NSLog(@"STR:%p recount = %ld",str,[str retainCount]);
NSLog(@"CPstr:%p recount = %ld",cpstr,[cpstr retainCount]);
2015-08-06 15:49:27.503 TableViewDemo[58085:1661052] STR:0x7fdc62739250 recount = 1
2015-08-06 15:49:27.504 TableViewDemo[58085:1661052] CPstr:0x7fdc62737f40 recount = 1
str与cpstr指向不同的对象,copy没有影响str引用计数。并且copy得到的对象是不可变的,所以不能改变cpstr。
- 可变对象的mutableCopy
NSMutableString *str = [NSMutableString stringWithFormat:@"123"];
NSMutableString *cpstr = [str mutableCopy];//深拷贝,str:123 ,cpstr:123
NSLog(@"STR:%p recount = %ld",str,[str retainCount]);
NSLog(@"CPstr:%p recount = %ld",cpstr,[cpstr retainCount]);
2015-08-06 15:53:21.852 TableViewDemo[58152:1664413] STR:0x7fe69b6515d0 recount = 1
2015-08-06 15:53:21.853 TableViewDemo[58152:1664413] CPstr:0x7fe69b653330 recount = 1
str与cpstr指向不同的对象,mutableCopy没有影响str引用计数。
总结:
- 不可变对象的copy是浅拷贝,就如retain性质一样,而mutableCopy则是深拷贝,新的内存拷贝。
- 可变对象的copy、mutableCopy都是深拷贝,内存的复制。需注意的是copy得到的对象是不可变的。
系统的容器类对象
首先,以上结论(即非容器类对象的拷贝规则)对系统容器类对象仍然是适用的,但是还是有变化。
- 不可变对象的copy
//copy返回不可变对象,mutablecopy返回可变对象
NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *arrayCopy = [array copy];
//arrayCopy1是和array同一个NSArray对象(指向相同的对象),包括array里面的元素也是指向相同的指针
NSLog(@"arrar:%p recount: %ld",array ,[array retainCount]);
NSLog(@"arraycopy:%p recount: %ld",arrayCopy,[arrayCopy retainCount]);
NSLog(@"object of array[0]:%p ,retaincount:%ld",array[0],[array[0] retainCount]);
NSLog(@"object of arrayCopy[0]:%p ,retaincount:%ld",arrayCopy[0],[arrayCopy[0] retainCount]);
2015-08-06 16:30:56.088 TableViewDemo[58790:1689296] arrar:0x7fd7fbf668a0 recount: 2
2015-08-06 16:30:56.088 TableViewDemo[58790:1689296] arraycopy:0x7fd7fbf668a0 recount: 2
2015-08-06 16:30:56.088 TableViewDemo[58790:1689296] object of array[0]:0x7fd7fbf64a80 ,retaincount:2
2015-08-06 16:30:56.089 TableViewDemo[58790:1689296] object of arrayCopy[0]:0x7fd7fbf64a80 ,retaincount:2
不但容器本身是浅拷贝,而且容器内的元素也是浅拷贝。
- 不可变对象的mutableCopy
NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSMutableArray *mArrayCopy = [array mutableCopy];
//mArrayCopy是array的可变副本,指向的对象和array不同,但是其中的元素和array1中的元素指向的是同一个对象。mArrayCopy还可以修改自己的对象
NSLog(@"arrar:%p recount: %ld",array ,[array retainCount]);
NSLog(@"mArraycopy:%p recount: %ld",mArrayCopy,[mArrayCopy retainCount]);
NSLog(@"object of array[0]:%p ,retaincount:%ld",array[0],[array[0] retainCount]);
NSLog(@"object of mArrayCopy[0]:%p ,retaincount:%ld",mArrayCopy[0],[mArrayCopy[0] retainCount]);
2015-08-06 16:38:13.488 TableViewDemo[58951:1696194] arrar:0x7fd902c1cb50 recount: 1
2015-08-06 16:38:13.497 TableViewDemo[58951:1696194] mArraycopy:0x7fd902c66e10 recount: 1
2015-08-06 16:38:13.497 TableViewDemo[58951:1696194] object of array[0]:0x7fd902c7e400 ,retaincount:3
2015-08-06 16:38:13.497 TableViewDemo[58951:1696194] object of mArrayCopy[0]:0x7fd902c7e400 ,retaincount:3
容器本身是深拷贝,但是容器内的元素却是浅拷贝。
- 可变对象的copy
NSMutableArray *mArray = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *arrayCppy = [mArray copy];
NSLog(@"mArray:%p recount: %ld",mArray ,[mArray retainCount]);
NSLog(@"array:%p recount: %ld",arrayCppy,[arrayCppy retainCount]);
NSLog(@"object of mArray[0]:%p ,retaincount:%ld",mArray[0],[mArray[0] retainCount]);
NSLog(@"object of arrayCopy[0]:%p ,retaincount:%ld",arrayCppy[0],[arrayCppy[0] retainCount]);
2015-08-06 16:48:23.686 TableViewDemo[59111:1703503] mArray:0x7fdef85076c0 recount: 1
2015-08-06 16:48:23.687 TableViewDemo[59111:1703503] array:0x7fdef8514a10 recount: 1
2015-08-06 16:48:23.687 TableViewDemo[59111:1703503] object of mArray[0]:0x7fdef850f300 ,retaincount:3
2015-08-06 16:48:23.687 TableViewDemo[59111:1703503] object of arrayCopy[0]:0x7fdef850f300 ,retaincount:3
容器本身是深拷贝,但是容器内的元素确是浅拷贝。
- 可变对象的mutableCopy
NSMutableArray *mArray = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSMutableArray *mMArrayCopy = [mArray mutableCopy];
NSLog(@"mArray:%p recount: %ld",mArray ,[mArray retainCount]);
NSLog(@"mMArrayCopy:%p recount: %ld",mMArrayCopy,[mMArrayCopy retainCount]);
NSLog(@"object of mArray[0]:%p ,retaincount:%ld",mArray[0],[mArray[0] retainCount]);
NSLog(@"object of mMArrayCopy[0]:%p ,retaincount:%ld",mMArrayCopy[0],[mMArrayCopy[0] retainCount]);
2015-08-06 16:50:28.608 TableViewDemo[59165:1705342] mArray:0x7f8aba59b440 recount: 1
2015-08-06 16:50:28.608 TableViewDemo[59165:1705342] mMArrayCopy:0x7f8aba59edf0 recount: 1
2015-08-06 16:50:28.609 TableViewDemo[59165:1705342] object of mArray[0]:0x7f8aba59aff0 ,retaincount:3
2015-08-06 16:50:28.609 TableViewDemo[59165:1705342] object of mMArrayCopy[0]:0x7f8aba59aff0 ,retaincount:3
容器本身是深拷贝,但是容器内的元素却是浅拷贝。
总结:
- 不管是可变对象,还是不可变对象,也不管是copy,还是mutableCopy,容器内元素始终是浅拷贝。
-
不可变对象的copy,容器本身是浅拷贝。可变对象的copy,容器本身是深拷贝;
不管是可变对象还是不可变对象,其mutableCopy容器本身都是深拷贝。
-
那么,如何拷贝容器内的元素呢?
元素的深拷贝
- 方法一:不但容器本身深拷贝,而且其元素也实现了深拷贝,但是其不可变对象仍然是浅拷贝;
NSArray *deepCopyArray=[[NSArray alloc] initWithArray:someArray copyItems:YES];
示例:
NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *deepCopyArray=[[NSArray alloc] initWithArray:array copyItems:YES];
NSLog(@"object of array[0]:%p ,retaincount:%ld",array[1],[array[1] retainCount]);
NSLog(@"object of deepCopyArray[0]:%p ,retaincount:%ld",deepCopyArray[1],[deepCopyArray[1] retainCount]);
NSLog(@"object of array[1]:%p ,retaincount:%ld",array[1],[array[1] retainCount]);
NSLog(@"object of deepCopyArray[1]:%p ,retaincount:%ld",deepCopyArray[1],[deepCopyArray[1] retainCount]);
2017-08-18 15:46:25.560 CopyLearn[30825:19466602] array:0x61000004e490 recount: 1
2017-08-18 15:46:25.560 CopyLearn[30825:19466602] deepCopyArray:0x61000004df80 recount: 1
2017-08-18 15:46:25.560 CopyLearn[30825:19466602] object of array[0]:0x61000006f640 ,retaincount:2
2017-08-18 15:46:25.562 CopyLearn[30825:19466602] object of deepCopyArray[0]:0xa000000000000611 ,retaincount:-1
2017-08-18 15:46:25.562 CopyLearn[30825:19466602] object of array[1]:0x10dd37080 ,retaincount:-1
2017-08-18 15:46:25.562 CopyLearn[30825:19466602] object of deepCopyArray[1]:0x10dd37080 ,retaincount:-1
可以看出,针对可变对象的copy消息,会实现深拷贝,但是针对@“b”这样的常量字符串,其本质上是浅拷贝。
再进一步,将array修改如下:
NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@[@"b"],@"c",nil];
此时,log显示如下:
2017-08-18 15:47:52.331 CopyLearn[31018:19471465] array:0x600000245010 recount: 1
2017-08-18 15:47:52.331 CopyLearn[31018:19471465] deepCopyArray:0x600000244d10 recount: 1
2017-08-18 15:47:52.331 CopyLearn[31018:19471465] object of array[0]:0x600000079440 ,retaincount:2
2017-08-18 15:47:52.331 CopyLearn[31018:19471465] object of deepCopyArray[0]:0xa000000000000611 ,retaincount:-1
2017-08-18 15:47:52.331 CopyLearn[31018:19471465] object of array[1]:0x600000004770 ,retaincount:3
2017-08-18 15:47:52.332 CopyLearn[31018:19471465] object of deepCopyArray[1]:0x600000004770 ,retaincount:3
此时,@[@"b"]是一个数组,不可变数组,此时仍然是浅拷贝!
所以,这种深拷贝,其不可变对象并未实现深拷贝。
- 方法二:
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData: [NSKeyedArchiver archivedDataWithRootObject:oldArray]];
实例:
NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData: [NSKeyedArchiver archivedDataWithRootObject:array]];
NSLog(@"array:%p recount: %ld",array ,[array retainCount]);
NSLog(@"trueDeepCopyArray:%p recount: %ld",trueDeepCopyArray,[trueDeepCopyArray retainCount]);
NSLog(@"object of array[0]:%p ,retaincount:%ld",array[0],[array[0] retainCount]);
NSLog(@"object of trueDeepCopyArray[0]:%p ,retaincount:%ld",trueDeepCopyArray[0],[trueDeepCopyArray[0] retainCount]);
NSLog(@"object of array[1]:%p ,retaincount:%ld",array[1],[array[1] retainCount]);
NSLog(@"object of trueDeepCopyArray[1]:%p ,retaincount:%ld",trueDeepCopyArray[1],[trueDeepCopyArray[1] retainCount]);
2017-08-18 15:52:21.947 CopyLearn[31140:19482635] array:0x600000242670 recount: 1
2017-08-18 15:52:21.947 CopyLearn[31140:19482635] trueDeepCopyArray:0x600000242f10 recount: 1
2017-08-18 15:52:21.947 CopyLearn[31140:19482635] object of array[0]:0x60000006e180 ,retaincount:2
2017-08-18 15:52:21.948 CopyLearn[31140:19482635] object of trueDeepCopyArray[0]:0x60000006e240 ,retaincount:1
2017-08-18 15:52:21.948 CopyLearn[31140:19482635] object of array[1]:0x10d815080 ,retaincount:-1
2017-08-18 15:52:21.948 CopyLearn[31140:19482635] object of trueDeepCopyArray[1]:0xa000000000000621 ,retaincount:-1
此时,数组内所有元素均实现了真正意义上的深拷贝,包括常量。
那么,针对不可变对象是否也是如此,答案是肯定的。
或许你还有一个疑问,常量的retainCount 为什么会是-1。☞指路
网友评论