1. 程式人生 > >iOS 之深拷貝與淺拷貝

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等等一類的物件。

1234NSString *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]);

執行結果:

可以看到陣列元素的地址不一樣。


轉載自: