iOS中的深复制与浅复制
很多语言中都有深复制浅复制的概念,如C++,ObjC等。简单来说,浅复制就是两个变量指向了同一块内存区域,深复制就是两个变量指向了不同的内存区域,但是两个内存区域里面的内容是一样的。
浅复制示意图:
深复制示意图:
iOS开发中,浅复制和深复制要更复杂一些,涉及到集合对象和非集合对象的copy与mutableCopy。
非集合对象:如NSString,NSInteger,NSNumber……
集合对象:如NSArray,NSDictionary,……
1:非集合对象的copy与mutableCopy。
非集合对象的copy与mutableCopy,只需要遵循以下规则即可:
(1)可变对象的copy和mutableCopy方法都是深复制
(2)不可变对象的copy方法是浅复制,mutableCopy方法是深复制
(3)copy方法返回的对象是不可变对象
下面通过代码来验证:
可变对象的copy与 mutableCopy方法:
void testMutable() { //可变对象的复制,copy和mutableCopy都是深拷贝 NSMutableString *str1 = [NSMutableString stringWithString:@"test"]; NSMutableString *str2 = [str1 copy]; //copy返回的是不可变对象,因此str2不能改变,会发生崩溃 //[str2 appendString:@"test"]; NSMutableString *str3 = [str1 mutableCopy]; [str3 appendString:@"test"]; NSLog(@"%@ %@ %@",str1,str2,str3); NSLog(@"%p %p %p",str1,str2,str3); }
执行结果:
可以看到,三个字符串的地址是不相同的,说明copy和 mutableCopy方法都是深复制。
不可变对象的copy与mutableCopy方法:
void testNoMutable() { NSString *str1 = @"test"; //直接copy是浅复制 NSMutableString *str2 = [str1 copy]; //copy返回的是不可变对象,str2不能被修改,因此会发生崩溃 //[str2 appendString:@"test"]; //mutableCopy是深复制 NSMutableString *str3 = [str1 mutableCopy]; [str3 appendString:@"test"]; NSLog(@"%@ %@ %@",str1,str2,str3); NSLog(@"%p %p %p",str1,str2,str3); }
执行结果:
可以看到:前两个地址一样,第三个地址不一样,因此不可变对象的copy方法是浅复制,mutableCopy方法是深复制。
另外需要注意: 无论是可变对象还是不可变对象,copy 方法返回的对象都是不可变的。
2:集合对象的copy与mutableCopy
实际上,集合对象与非集合对象所遵循的规则基本上是一样的。
可变对象的的copy与mutableCopy 方法
验证代码:
void testSetMutable() { NSMutableArray *array1 = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c", nil]; //可变对象copy是深复制 NSMutableArray *array2 = [array1 copy]; //copy返回的是不可变对象,array2不能被修改,因此会崩溃 //[array2 addObject:@"d"]; //可变对象的mutableCopy是深复制 NSMutableArray *array3 = [array1 mutableCopy]; [array3 addObject:@"d"]; NSLog(@"%p %p %p",array1,array2,array3); }
执行结果:
可以看到地址是不一样的。说明可变对象的copy和mutableCopy方法都是深复制。
不可变对象的copy与mutableCopy方法
验证代码:
void testSetNoMutable() { NSArray *array1 = @[@"a",@"b",@"c"]; //不可变对象的copy方法,浅复制 NSArray *array2 = [array1 copy]; //不可变对象的mutableCopy方法,深复制 NSArray *array3 = [array1 mutableCopy]; NSLog(@"%p %p %p",array1,array2,array3); }
执行结果:
可以看到,前两个地址一样,第三个地址不一样。说明不可变对象的copy方法是浅复制,mutableCopy方法是深复制。
集合对象和非集合对象的一个差别:
上面说的集合对象的深复制并不是严格意义上的深复制,而是单层深复制。
单层深复制:对集合对象来说,深复制时只是将第一层对象进行了深复制,内部的对象仍然是浅复制。比如说
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c", nil];
调用copy方法 NSArray *array2 = [array1 copy] ,有分配了一块内存,array2指向了这块内存,但是数组内部的元素,指向的仍然是数组1内部的元素,即内部元素是浅复制。
验证代码:
void testSetMutable() { NSMutableArray *array1 = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c", nil]; //可变对象copy是深复制 NSMutableArray *array2 = [array1 copy]; //copy返回的是不可变对象,array2不能被修改,因此会崩溃 //[array2 addObject:@"d"]; //可变对象的mutableCopy是深复制 NSMutableArray *array3 = [array1 mutableCopy]; [array3 addObject:@"d"]; NSLog(@"%p %p %p",array1,array2,array3); NSLog(@"%p %p %p",array1[0],array2[0],array3[0]); }
执行结果:
可以看到,三个数组的第一个元素的地址是一样的,也就是说内部元素是浅复制。
3:集合对象的完全复制
集合对象的完全复制,就是集合中的每一层的元素都是深复制。
方法一:
使用 initWith***: copyItems:YES 方法。
代码:
void testFullCopy() { NSMutableArray *array1 = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c", nil]; NSArray *array2 = [[NSArray alloc] initWithArray:array1 copyItems:YES]; NSLog(@"%p %p",array1,array2); NSLog(@"%p %p",array1[0],array2[0]); }
执行结果:
可以看到数组元素的地址不一样。
方法二:
先将集合进行归档,然后再解档。代码如下:
void testFullCopy2() { NSMutableArray *array1 = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c", nil]; NSArray *array2 = [NSKeyedUnarchiver unarchiveTopLevelObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:array1 ] error:nil]; NSLog(@"%p %p",array1,array2); NSLog(@"%p %p",array1[0],array2[0]); }
执行结果:
可以看到数组元素的地址不一样。