很多语言中都有深复制浅复制的概念,如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[],array2[],array3[]);
}
执行结果:
可以看到,三个数组的第一个元素的地址是一样的,也就是说内部元素是浅复制。
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[],array2[]);
}
执行结果:
可以看到数组元素的地址不一样。
方法二:
先将集合进行归档,然后再解档。代码如下:
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[],array2[]);
}
执行结果:
可以看到数组元素的地址不一样。