OC學習篇之---對象的拷貝,oc---拷貝
在前一篇文章中我們說到了如何解決對象的循環參考問題:http://blog.csdn.net/jiangwei0910410003/article/details/41926369,這一篇文章我們就來介紹一下OC中的對象拷貝概念,這個對於物件導向語言中都會有這種的問題,只是不同的語言有不同的解決方式:C++中有拷貝建構函式,Java中需要實現Cloneable介面,在clone方法中進行操作。但是不過OC更偏向於Java這種方式,OC中如果一個對象需要被拷貝,他需要實現協議:
<NSCopying>、<NSMutableCopying>
從名字上我們可以看到,一個協議是用於不可變對象的,一個協議適用於可變對象的
首先來介紹一下對象的拷貝的概念吧:
為什麼要由對象的拷貝這麼一個概念呢?看一個情境:假如現在一個對象中又一個數組對象,現在我們產生一個對象,同時將這個對象賦值給另外一個對象,那麼現在問題是這兩個對象中的數組對象是同一個,那麼如果一個對象中去修改這個數值中的內容,另外一個對象中的數組內容也會被修改,相當於這個數組對象是共用的,當然我們有時候是不希望這種形式的出現的,這時候我們就出現了對象的拷貝。
具體來看一個例子吧
一、系統類別對象的拷貝
//// main.m// 30_CopyObject//// Created by jiangwei on 14-10-13.// Copyright (c) 2014年 jiangwei. All rights reserved.//#import <Foundation/Foundation.h>/** */int main(int argc, const char * argv[]) { @autoreleasepool { //對象具備拷貝功能,必須實現如下協議 //<NSCopying>、<NSMutableCopying> //copy方法返回的是一個不可變對象,mutableCopy方法返回的是一個可變對象 /* NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"one",@"two",nil]; NSMutableArray *array2 = [array1 retain]; //retain只是引用計數+1,沒有建立新的對象 //array1與array2指標相同,指向同一個對象 if(array1 == array2){ NSLog(@"array1 == array2"); NSLog(@"array1的引用計數:%ld",array1.retainCount); } */ NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"one",@"two",nil]; //複製對象,建立一個新的副本對象 //這裡使用copy方法複製,返回的是一個不可變數組,但是用一個可變數組來聲明,但是我們關心的是指標的的內容,而不是類型 //所以array2的真實類型還是不可變類型的 NSMutableArray *array2 = [array1 copy];//array2計數為:1,因為是新建立出來的對象 //使用mutableCopy方法,返回的就是可變數組 //當然這種方法只針對於那些有可變對象之分有用,對於其他的對象這個方法和copy方法的效果是一樣的 NSMutableArray *array3 = [array1 mutableCopy]; if(array1 != array2){ NSLog(@"array1 != array2"); NSLog(@"array1的引用計數:%ld",array1.retainCount); NSLog(@"array2的引用計數:%ld",array2.retainCount); } [array2 release]; [array1 release]; } return 0;}我們看到,NSMutableArray有一個mutableCopy方法,這樣返回的一個數組對象就是一個拷貝對象了。
但是這裡需要注意的是:
copy方法和mutableCopy方法的區別
這兩個方法的區別只在於那些有可變對象和不可變對象之分的對象上,對於沒有這種區分的對象來說,這兩個方法的效果是一樣的。
[不可變對象 copy]是假拷貝,等價於[不可變對象 retain]
[不可變對象 mutableCopy是真拷貝
二、深拷貝和淺拷貝
在拷貝對象中也是有深拷貝和淺拷貝之分的
淺拷貝:只拷貝所有屬性對象的指標
深拷貝:拷貝屬性對象的內容
看個例子:
Person.h
//// Person.h// 31_DeepCopy//// Created by jiangwei on 14-10-13.// Copyright (c) 2014年 jiangwei. All rights reserved.//#import <Foundation/Foundation.h>@interface Person : NSObject <NSCopying>@property(nonatomic,retain)NSMutableArray *apples;@property(nonatomic)int age;@end
Person.m
//// Person.m// 31_DeepCopy//// Created by jiangwei on 14-10-13.// Copyright (c) 2014年 jiangwei. All rights reserved.//#import "Person.h"@implementation Person- (id)copyWithZone:(NSZone *)zone{ //建立一個新的副本對象 //這個方法是會被繼承的,所以這裡還是不用 //[Person allocWithZone:<#(struct _NSZone *)#>]; Person * p = [[self class] allocWithZone:zone]; //p.apples = _apples;//是指標賦值,所以還是淺拷貝 //深拷貝 //拷貝之後引用計數會+1,需要release以下 p.apples = [_apples mutableCopy]; p.age = _age; [p.apples release]; //但是如果我們使用->文法就不需要了,因為我們沒有使用set方法,引用計數沒有操作 //但是這種方式我們不採用 //p->_apples = [_apples mutableCopy]; return p;}@end
我們看到,Person實現了NSCopying協議,然後需要實現一個方法:copyWithZone
在這個方法中我們開始進行拷貝操作:
Person類中有一個屬性類型是數組
這裡我們需要產生一個Person對象,然後進行屬性的拷貝,最後在返回這個對象
淺拷貝:直接複製數組指標
深拷貝:直接複製數組的內容,這裡可以直接使用mutableCopy方法進行實現
測試類別
main.m
//// main.m// 31_DeepCopy//// Created by jiangwei on 14-10-13.// Copyright (c) 2014年 jiangwei. All rights reserved.//#import <Foundation/Foundation.h>#import "Person.h"//深拷貝和淺拷貝//預設是淺拷貝int main(int argc, const char * argv[]) { @autoreleasepool { NSMutableArray *array1 = [NSMutableArray arrayWithCapacity:2]; for(int i=0;i<2;i++){ Person *p = [[Person alloc] init]; [array1 addObject:p]; [p release]; } //引用計數都是1 for(Person *p in array1){ NSLog(@"複製之前的引用計數:%ld",p.retainCount); NSLog(@"複製之前的指標:%p",p); } //引用計數都是2,因為是淺拷貝,又有指標指向對象了,array2也是使用了person //淺拷貝:只拷貝對象指標 //深拷貝:複製屬性 NSArray *array2 = [array1 copy]; for(Person *p in array2){ NSLog(@"複製之前的引用計數:%ld",p.retainCount); NSLog(@"複製之前的指標:%p",p); } //這裡Person中有一個屬性是NSMutableArray,但是我們只是賦值,並不是拷貝 //所以這裡還不算是深拷貝 Person *p = [[Person alloc] init]; p.apples = [NSMutableArray arrayWithObjects:@"iphone",@"ipad", nil]; p.age = 20; Person *p1 = [p copy]; if(p != p1){ NSLog(@"p1.age=%d",p1.age); NSLog(@"p1.apples=%@",p1.apples); } } return 0;}
三、字串的拷貝
//// main.m// 32_NSStringCopy//// Created by jiangwei on 14-10-13.// Copyright (c) 2014年 jiangwei. All rights reserved.//#import <Foundation/Foundation.h>#import "Person.h"//字串為什麼使用copyint main(int argc, const char * argv[]) { @autoreleasepool { Person *p = [[Person alloc] init]; NSMutableString *name = [NSMutableString stringWithString:@"jack"]; p.name = name; //人的名字被修改了 //如果Person的name是retain,則此處的name和person對象的name執行的是同一個字串對象 //此處的name修改之後,會導致person的name也被修改,破壞了person對象的封裝性 //正常情況下,我們會使用set方法設定名字 //所以如果使用的是copy的話,就不會修改名字了 [name appendString:@"-tom"]; //Foundation架構中可複製的對象,當我們拷貝的是一個不可變對象時候 //他的作用相當於retain(系統做的記憶體最佳化) //所以這裡的如果換成NSString類型的時候,其實沒有拷貝的動作的,因為NSString是不可變的 //但是使用mutableCopy就可以做到拷貝了,mutableCopy是真正意義上的拷貝 //mutableCopy拷貝方法,不管什麼對象都是真實拷貝 //[不可變對象 copy]是假拷貝,等價於[不可變對象 retain] //[不可變對象 mutableCopy是真拷貝 } return 0;}這裡為什麼要單獨說一下字串的拷貝呢?
因為字串是一個特殊的對象,我們應該調用他的copy方法。因為我們對於字串其實我們是期望他只有一分值得,就看上面的例子:
我們用NSMutableString產生一個name,然後將其賦值給person對象,當我們在外面修改name的內容的時候,其實person的name屬性的值也應該修改。所以我們一般在拷貝字串對象的時候,都會調用他的copy方法
總結
這一篇文章主要介紹了OC中對象拷貝的相關概念和知識點。我們在操作對象的時候,有時候進行拷貝,還要仔細考慮一下是深拷貝還是淺拷貝。