記住:
1.對于容器類本身,與非容器類對象的結論相同,即
如果對一不可變對象複制,copy是指針複制(淺拷貝)和mutableCopy就是對象複制(深拷貝)。
如果是對可變對象複制,都是深拷貝,但是copy傳回的對象是不可變的。
關鍵要注意的是複制後容器内對象的變化,比如添加、删除元素,修改某個元素的值。
2.對于容器而言,其元素對象始終是指針複制。如果需要元素對象也是對象複制,就需要實作深拷貝。
開發中經常遇到這個問題,不過發現網上能完整的總結的還是很少。今天就動筆把這個以我的角度總結一下。盡量把問題說明白。。
(一)關于copy和mutableCopy
顧名思義,copy就是複制了一個imutable的對象,而mutablecopy就是複制了一個mutable的對象。
一個NSObject的對象要想使用這兩個函數,那麼類必須實作NSCopying協定和NSMutableCopying協定。
對于NSCopying,實作+ copyWithZone:方法。
對于NSMutableCopying,實作+ mutableCopyWithZone:方法。
當然這種情況一般是在自定義結構體時會用,這個我會在後面舉例說一下。但經常用的NSString,NSArray,NSDictionary等系統提供的結構體都已實作。
(二)NSString、NSMutableString、NSArray、NSMutableArray分别進行copy和mutableCopy時的情況
上面所說的四種結構體可以分為兩種類型。
1.系統的非容器類對象
這裡指的是NSString,NSNumber等等一類的對象。
2.系統的容器類對象
指NSArray,NSDictionary等。
下面對這4種結構體分别進行舉例,分為8總情況。我會配上相應的記憶體截圖。以友善閱讀。
例1:對NSString進行copy和mutableCopy操作
NSString *originStr = @"origin";
NSString *originStrCopy = [originStr copy];
NSMutableString *originStrMutableCopy = [originStr mutableCopy];
記憶體截圖:
例2:對NSMutableString進行copy和mutableCopy操作
NSMutableString *mutableOriginStr =[NSMutableString stringWithString: @"mutableOrigin"];
//注意這裡下面兩行代碼前面的聲明
NSString *mutableOriginStrCopy = [mutableOriginStr copy];
NSMutableString *mutableOriginStrCopy1 = [mutableOriginStr copy];
//這裡會報錯,因為copy生成的是imutable對象,是以不管聲明是什麼樣的,依舊是imutable的
[mutableOriginStrCopy1 appendString:@"error"];
NSMutableString *mutableOriginStrMutableCopy = [mutableOriginStr mutableCopy];
記憶體截圖:
總結:
對于系統的非容器類對象,
如果對一不可變對象複制,copy是指針複制(淺拷貝)和mutableCopy就是對象複制(深拷貝)。
如果是對可變對象複制,都是深拷貝,但是copy傳回的對象是不可變的。
例3:對NSArray進行copy和mutableCopy操作
NSArray *originArray = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
NSArray *originArrayCopy = [originArray copy];
//originArray是和originArray同一個NSArray對象(指向相同的對象),包括originArray裡面的元素也是指向相同的指針,此處可以看到copy的作用也相當于retain
NSLog(@"originArray retain count: %ld",(unsigned long)[originArray retainCount]);
NSLog(@"originArray retain count: %ld",(unsigned long)[originArrayCopy retainCount]);
NSMutableArray *originArrayMutableCopy = [originArray mutableCopy];
//originArrayMutableCopy是originArray的可變副本,指向的對象和originArray不同,但是其中的元素和originArray中的元素指向的是同一個對象。originArrayMutableCopy還可以修改自己的對象
[originArrayMutableCopy addObject:@"d"];
[originArrayMutableCopy removeObjectAtIndex:0];
記憶體截圖:
1.操作前:
2.添加一個元素後:
3.删除一個元素後:
不知我的xcode怎麼回事,對于originArrayMutableCopy顯示貌似有點問題,打出來結果如下:
2013-03-21 16:08:08.885 MemoryTest[52329:303] originArrayMutableCopy (
b,
c,
d
)
總結:
originArray和originArrayCopy是指針複制,而originArrayMutableCopy是對象複制,originArrayMutableCopy還可以改變期内的元素:删除或添加。但是注意的是,容器内的元素内容都是指針複制。
例4:修改元素的值
NSArray *originArray = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"], @"b", @"c", nil];
NSArray *originArrayCopyArray = [originArray copy];
NSMutableArray *originArrayMutableCopyArray = [originArray mutableCopy];
// originArrayMutableCopyArray,originArrayCopyArray和originArray其中的元素都是一樣的對象——同一個指針
//注意:這裡隻能是将值先取出來通過append來修改,如果用testString = @"d";這樣會改變testString的指針,其實是将@“d”臨時對象賦給了testString
NSMutableString *testString = [originArray objectAtIndex:0];
[testString appendString:@" changevalue"];// 這樣以上三個數組的首元素都被改變了
記憶體截圖
1.修改值之前
2.修改值之後
3.用testString = @"d"修改值後記憶體情況,這樣以數組中的元素沒有任何影響,當然也沒有任何意義
例5:對NSMutableArray進行copy和mutableCopy操作
NSMutableArray *originArray = [NSMutableArray arrayWithObjects:@"a",@"b",@"c", nil];
NSArray *originArrayCopy = [originArray copy];
NSMutableArray *originArrayCopy1 = [originArray copy];
[originArrayCopy1 addObject:@"d"];//mutable對象copy之後成為imutable對象,是以添加元素會error
NSMutableArray *originArrayMutableCopy = [originArray mutableCopy];
記憶體截圖
總結:
1.對于容器類本身,與非容器類對象的結論相同,即
如果對一不可變對象複制,copy是指針複制(淺拷貝)和mutableCopy就是對象複制(深拷貝)。
如果是對可變對象複制,都是深拷貝,但是copy傳回的對象是不可變的。
關鍵要注意的是複制後容器内對象的變化,比如添加、删除元素,修改某個元素的值。
2.對于容器而言,其元素對象始終是指針複制。如果需要元素對象也是對象複制,就需要實作深拷貝。
例6:深拷貝的一種實作
NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],[NSString stringWithString:@"b"],@"c",nil];
NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array]];
記憶體截圖:
總結:
1.trueDeepCopyArray是完全意義上的深拷貝,而deepCopyArray則不是,對于deepCopyArray内的不可變元素其還是指針複制
2.[deepCopyArray objectAtIndex:0]因為原來是可變對象,還和上面的結論一樣,依舊是對象拷貝。
3.用歸檔的方法實作了真正的元素對象拷貝。
4.記得在iphone3開發基礎教程上也有深拷貝的一個方法,下次再總結上來。