Apple官方文檔如下:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Collections/Articles/Copying.html
最近用Core Data做數據管理,Fetch到NSArray類型的結果數組,爲了能夠進行增、刪、改,我把它MutableCopy到一個NSMutableArray數組中。增加、刪除用manageModelsContext中的接口操作即可,修改則把NSArray結果中需要修改的實例取出來進行更改,然後用manageModelsContext保存即可完成修改到sqlite文件。
可是問題是我發現用MutableCopy得到的數組中取出的元素直接修改後保存,也能完成修改操作。於是就產生了這樣的一個疑問:MutableCopy是淺拷貝??再下來我就發現copy相關的東西我還完全沒有搞清楚。
首先關於copy和mutableCopy的行爲:不管是NSString這種元素類、還是NSArray這樣的容器類、還是Mutable和非Mutable類,copy和mutableCopy調用後表現的行爲到底是什麼樣完成取決於類本身NSCopying和NSMutableCopying協議是如何實現的。
想要正常調用copy和mutableCopy兩個函數,那麼類就一定要實現對應的協議。
1. 元素數據的copy和mutableCopy。
常用的NSString類,示例代碼如下:
[cpp] view plaincopy
- NSString* string = @”a”;
- NSString* stringCopy = [string copy];// stringCopy與string地址相同,retainCount+ 1
- NSMutableString* stringMCopy = [string mutablecopy];// stringMCopy與string地址不同
- NSMutableString* stringM1 = [stringMCopy copy];//地址與stringMCopy不同,且爲不可修改
- NSMutableString* stringM2 = [stringMCopy mutablecopy];//地址與stringMCopy不同,可修改
[cpp] view plaincopy
- NSString* string = @”a”;
- NSString* stringCopy = [string copy];// stringCopy與string地址相同,retainCount+ 1
- NSMutableString* stringMCopy = [string mutablecopy];// stringMCopy與string地址不同
- NSMutableString* stringM1 = [stringMCopy copy];//地址與stringMCopy不同,且爲不可修改
- NSMutableString* stringM2 = [stringMCopy mutablecopy];//地址與stringMCopy不同,可修改
可以基本推出NSString和NSMutableString中兩個協議的實現
[cpp] view plaincopy
- NSString:
- - (id)copywithZone:(NSZone*)zone
- {
- return self;
- }
- - (id)mutableCopywithZone:(NSZone*)zone
- {
- NSMutableString* copy =[[NSMutableString alloc] initxxxxxx];
- ....
- return copy;
- }
- NSMutableString:
- - (id)copywithZone:(NSZone*)zone
- {
- NSString* copy = [[NSStringalloc] initxxxxxx];
- ....
- return copy;//所以不可修改
- }
- - (id)mutableCopywithZone:(NSZone*)zone
- {
- NSMutableString* copy =[[NSMutableString alloc] initxxxxxx];
- ....
- return copy;
- }
[cpp] view plaincopy
- NSString:
- - (id)copywithZone:(NSZone*)zone
- {
- return self;
- }
- - (id)mutableCopywithZone:(NSZone*)zone
- {
- NSMutableString* copy =[[NSMutableString alloc] initxxxxxx];
- ....
- return copy;
- }
- NSMutableString:
- - (id)copywithZone:(NSZone*)zone
- {
- NSString* copy = [[NSStringalloc] initxxxxxx];
- ....
- return copy;//所以不可修改
- }
- - (id)mutableCopywithZone:(NSZone*)zone
- {
- NSMutableString* copy =[[NSMutableString alloc] initxxxxxx];
- ....
- return copy;
- }
2. 容器類的copy和mutableCopy。
常用類NSArray和NSMutableArray,看如下示例代碼:
[cpp] view plaincopy
- Class1* obj1= ....;//正常初始化
- NSArray* array = [[NSArray alloc] initWithObjects:obj1, nil];
- NSArray* arrayCopy = [array copy];//地址不變,retaincount+1
- NSMutableArray* arrayMCopy = [array mutableCopy];//地址改變,但是數組中成員指針和obj1相同,淺拷貝
- NSMutableArray* arrayM1 = [arrayMCopy Copy];//地址改變,但是數組中成員指針和obj1相同,淺拷貝。arrayM1爲NSArray不可修改
- NSMutableArray* arrayM2 = [arrayMCopy mutableCopy];//地址改變,但是數組中成員指針和obj1相同,淺拷貝
[cpp] view plaincopy
- Class1* obj1= ....;//正常初始化
- NSArray* array = [[NSArray alloc] initWithObjects:obj1, nil];
- NSArray* arrayCopy = [array copy];//地址不變,retaincount+1
- NSMutableArray* arrayMCopy = [array mutableCopy];//地址改變,但是數組中成員指針和obj1相同,淺拷貝
- NSMutableArray* arrayM1 = [arrayMCopy Copy];//地址改變,但是數組中成員指針和obj1相同,淺拷貝。arrayM1爲NSArray不可修改
- NSMutableArray* arrayM2 = [arrayMCopy mutableCopy];//地址改變,但是數組中成員指針和obj1相同,淺拷貝
[cpp] view plaincopy
- //推斷
[cpp] view plaincopy
- //推斷
[cpp] view plaincopy
- NSArray:
- - (id)copywithZone:(NSZone*)zone
- {
- //僞碼
- return [self retain];
- }
- - (id)mutableCopywithZone:(NSZone*)zone
- {
- NSMutableArray* copy = [[NSMutableString alloc] initxxxxxx];
- for (id element in self) {
- [copy addObject:element];//element retian count + 1
- ....
- }
- return copy;
- }
- NSMutableArray:
- - (id)copywithZone:(NSZone*)zone
- {
- NSArray* copy = [[NSArray alloc] initXXX];
- /*把每個element加入到copy數組,retainCount+1*/
- ....
- return copy;
- }
- - (id)mutableCopywithZone:(NSZone*)zone
- {
- NSMutableArray* copy = [[NSMutableString alloc] initxxxxxx];
- for (id element in self) {
- [copy addObject:element];//element retian count + 1
- ....
- }
- return copy;
- }
[cpp] view plaincopy
- NSArray:
- - (id)copywithZone:(NSZone*)zone
- {
- //僞碼
- return [self retain];
- }
- - (id)mutableCopywithZone:(NSZone*)zone
- {
- NSMutableArray* copy = [[NSMutableString alloc] initxxxxxx];
- for (id element in self) {
- [copy addObject:element];//element retian count + 1
- ....
- }
- return copy;
- }
- NSMutableArray:
- - (id)copywithZone:(NSZone*)zone
- {
- NSArray* copy = [[NSArray alloc] initXXX];
- /*把每個element加入到copy數組,retainCount+1*/
- ....
- return copy;
- }
- - (id)mutableCopywithZone:(NSZone*)zone
- {
- NSMutableArray* copy = [[NSMutableString alloc] initxxxxxx];
- for (id element in self) {
- [copy addObject:element];//element retian count + 1
- ....
- }
- return copy;
- }
3. 深拷貝
上面提到的官方文檔中介紹兩種實現深拷貝的方法:
a. 用Array的initWithArray: copyItems函數,如下:
NSArray *deepCopyArray=[[NSArray alloc] initWithArray: someArraycopyItems: YES];
調用後,會對原NSArray中的每個元素調用其copy函數,並把返回的id加入到新的數組中。所以這是依賴於Obj對象類實現的深拷貝,如果- (id)copywithZone:(NSZone*)zone是重新分配一塊內存賦值後返回,那麼就是真正的深拷貝。如果直接返回自身,那麼它只是淺拷貝。
b. 用archiver方式:
NSArray* trueDeepCopyArray = [NSKeyedUnarchiverunarchiveObjectWithData: [NSKeyedArchiver archivedDataWithRootObject:oldArray]];
這是真正意義上的深拷貝,不依賴於實際類Copying協議的實現。
4. 用Category實現自定義的深拷貝deepmutableCopy,如:
[cpp] view plaincopy
- - (NSMutableArray *)mutableDeepCopy
- {
- NSMutableArray *ret = [[NSMutableArrayalloc] initWithCapacity:[self count]];
- for (id value in self)
- {
- id oneCopy = nil;
- if ([value respondsToSelector:@selector(mutableDeepCopy)])
- oneCopy = [value mutableDeepCopy];
- else if ([value respondsToSelector:@selector(mutableCopy)])
- oneCopy = [value mutableCopy];
- if (oneCopy == nil)
- oneCopy = [value copy];
- [ret addObject: oneCopy];
- }
- return ret;
- }
[cpp] view plaincopy
- - (NSMutableArray *)mutableDeepCopy
- {
- NSMutableArray *ret = [[NSMutableArrayalloc] initWithCapacity:[self count]];
- for (id value in self)
- {
- id oneCopy = nil;
- if ([value respondsToSelector:@selector(mutableDeepCopy)])
- oneCopy = [value mutableDeepCopy];
- else if ([value respondsToSelector:@selector(mutableCopy)])
- oneCopy = [value mutableCopy];
- if (oneCopy == nil)
- oneCopy = [value copy];
- [ret addObject: oneCopy];
- }
- return ret;
- }