iOS 之深拷貝與淺拷貝
淺拷貝
淺拷貝就是對記憶體地址的複製,讓目標物件指標和源物件指向同一片記憶體空間。如:
char* str = (char*)malloc(100); char* str2 = str; |
iOS 裡面的淺拷貝:
在 iOS 裡面, 使用retain 關鍵字進行引用計數,就是一種更加保險的淺拷貝。他既讓幾個指標共用同一片記憶體空間,又可以在release 由於計數的存在,不會輕易的銷燬記憶體,達到更加簡單使用的目的。
深拷貝:
深拷貝是指拷貝物件的具體內容,而記憶體地址是自主分配的,拷貝結束之後,兩個物件雖然存的值是相同的,但是記憶體地址不一樣,兩個物件也互不影響,互不干涉。
copy 與 retain 的區別:
copy 是建立一個新物件,retain 是建立一個指標,引用物件計數加一。 copy屬性標識兩個物件內容相同,新的物件retain count為1, 與舊有物件引用計數無關,舊有物件沒有變化。copy減少物件對上下文的依賴。
iOS裡的深拷貝:
這裡指的是NSString, NSNumber等等一類的物件。
1234 | NSString *string = @”dddd"; NSString *stringCopy = [string copy]; NSMutableString *stringDCopy = [string mutableCopy]; [stringMCopy appendString:@ "!!" ]; 通過記憶體發現,string和stringCopy指向的是同一塊記憶體區域(weak reference),引用計數沒有發生改變。而stringMCopy則是我們所說的真正意義上的複製,系統為其分配了新記憶體,是兩個獨立的字串內容是一樣的。 |
拷貝構造:
當然在 ios 中並不是所有的物件都支援copy,mutableCopy,遵守NSCopying協議的類可以傳送copy訊息,遵守NSMutableCopying協議的類才可以傳送mutableCopy訊息。
假如傳送了一個沒有遵守上訴兩協議而傳送copy或者 mutableCopy,那麼就會發生異常。但是預設的ios類並沒有遵守這兩個協議。如果想自定義一下copy 那麼就必須遵守NSCopying,並且實現 copyWithZone: 方法,如果想自定義一下mutableCopy 那麼就必須遵守NSMutableCopying,並且實現 mutableCopyWithZone: 方法。
如果是我們定義的物件,那麼我們自己要實現NSCopying , NSMutableCopying這樣就能呼叫copy和mutablecopy了。舉個例子:
12345678 | @interface MyObj : NSObject<NSCopying, NSMutableCopying>{ NSMutableString *_name; NSString * _imutableStr ; int _age; } @property (nonatomic, retain) NSMutableString *name; @property (nonatomic, retain) NSString *imutableStr; @property (nonatomic) int age; |
copy拷貝構造:
1234567 | - (id)copyWithZone:(NSZone *)zone{ MyObj *copy = [[[self class] allocWithZone :zone] init]; copy->name = [_name copy]; copy->imutableStr = [_imutableStr copy]; copy->age = age; return copy; } |
mutableCopy拷貝構造:
123456 | - (id)mutableCopyWithZone:(NSZone *)zone{ MyObj *copy = NSCopyObject(self, 0, zone); copy->name = [_name mutableCopy]; copy->age = age; return copy; } //+++++++++++++++++++++++++++++++++iOS開發中,淺複製和深複製要更復雜一些,涉及到集合物件和非集合物件的copy與mutableCopy。 非集合物件:如NSString,NSInteger,NSNumber…… 集合物件:如NSArray,NSDictionary,…… 1:非集合物件的copy與mutableCopy。 非集合物件的copy與mutableCopy,只需要遵循以下規則即可: (1)可變物件的copy和mutableCopy方法都是深複製 (2)不可變物件的copy方法是淺複製,mutableCopy方法是深複製 (3)copy方法返回的物件是不可變物件 下面通過程式碼來驗證: 可變物件的copy與 mutableCopy方法: //可變物件的複製,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方法: 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 方法返回的物件都是不可變的。mu tablecopy方法返回的物件是可變的; 2:集合物件的copy與mutableCopy 實際上,集合物件與非集合物件所遵循的規則基本上是一樣的。 可變物件的的copy與mutableCopy 方法 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方法 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內部的元素,即內部元素是淺複製。 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:集合物件的完全複製 集合物件的完全複製,就是集合中的每一層的元素都是深複製。 方法一: 使用 [[NSArray alloc] initWithArray:array1 copyItems:YES]; 方法。 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]); 執行結果: 可以看到陣列元素的地址不一樣。 方法二: 先將集合進行歸檔,然後再解檔。程式碼如下: 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]); 執行結果: 可以看到陣列元素的地址不一樣。 轉載自: |