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]); }
執行結果:
可以看到數組元素的地址不一樣。