首先,從copy開始說,簡而言之,copy的目的就是生成一個新的實例,而後把其成員都按原實例賦值。對於非指針型的成員,好比BOOL, int, float,這樣的賦值能夠直接進行。可是對於指針型的數據,好比Objc中用到的對象,就有Deep Copy和Shallow Copy的區別——這個和在C++中的基本上是同樣的:是生成新的成員對象,或是指向同一成員對象。
瞭解了這點之後,再看看Copy在 Objetive-C中的實現方式。若是要調用一個對象的copy方法,這個對象必須遵循NSCopying的協議。這個協議中規定了一個方法:- (id)copyWithZone:(NSZone *)zone;咱們就是經過實現這個方法給對象提供拷貝的功能。對於不少現有類,如NSString,NSDictionary,。。。這個方法已經實 現。假設咱們如今自定義了一個類,須要爲這個類提供拷貝的功能,就須要本身來動手寫CopyWithZone的方法:示例以下:
這個是自定義的類:
@interface Product : NSObject <NSCopying>
{
NSString *productName;
float price;
id delegate;
}
@end
而後咱們須要在Product類中實現NSCopying中的copyWithZone方法:
- (id)copyWithZone:(NSZone *)zone
{
Product *copy = [[[self class] allocWithZone: zone]
initWithProductName:[self productName]
price:[self price]]; //注意這裏,咱們使用了class的allocWithZone的方法建立了一個拷貝,這裏假定Product類中有一個 initWithProductName: price:的初始化方法。那麼這樣調用後就獲得了一個Product的副本,並且name和price都已經設置好了
[copy setDelegate:[self delegate]]; //這裏再設置delegate
return copy; //返回副本
}
那麼這樣,若是咱們有一個product的實例, 假設爲product1,而後調用Product *product2 = [product1 copy];
就會使用咱們上面寫的copyWithZone的方法建立一個product1的副本,而後賦值給product2。
這裏再以上面方法中的成員delegate爲例,解釋一下deep copy和shallow copy:
在 copyWithZone方法中,咱們獲得了一個新的product實例,可是delegate是個對象,因此在副本中,咱們能夠選擇建立一個新的 delegate對象(deep copy),或是指向同一個delegate(shallow copy)。這個就取決於Product類中的setDelegate:方法了。你能夠選擇在setDelegate的時候,copy,也可讓它們都指 向同一個對象(可是須要retain,緣由能夠本身思考一下),固然,簡單assign在某種狀況下也是能夠的。
假設在Product類中有setDelegate:方法,或是有delegate的property:
- (void)setDelegate: (id)aDelegate
{
[delegate release];
delegate = [delegate copy];
}
這 樣就是一個深拷貝了,由於使用了delegate的copy方法獲得了一個delegate的副本。至於如何獲得delegate的副本,就要看 delegate的copyWithZone方法的實現了,不在這個層面的考慮中。也就是說,copy老是一中「遞歸」的形式,從上到下,咱們能夠一層一 層的考慮。
- (void)setDelegate: (id)aDelegate
{
[delegate release];
delegate = [aDelegate retain];
}
這樣操做後,delegate和aDelegate爲同一對象,可是爲了內存管理方面的要求,咱們調用了retain來將reference count加了一。固然,若是不須要了,還能夠直接賦值(assign):
- (void)setDelegate: (id)aDelegate
{
delegate = aDelegate
}
你能夠把這個例子本身實現一下,而後用log打一打內存,這個結構就很明瞭了。
而後再說一下可變副本(mutable copy)和不可變副本(immutable copy):
可變和不可變的概念,咱們以前經過NSDictionary和NSMutableDictionary的區別瞭解過。
一 般來講,若是咱們的某個類須要區別對待這兩個功能——同時提供建立可變副本和不可變副本的話,通常在NSCopying協議規定的方法 copyWithZone中返回不可變副本;而在NSMutableCopying的mutableCopyWithZone方法中返回可變副本。而後調 用對象的copy和mutableCopy方法來獲得副本。
舉個例子:
NSDictionary類已經遵循了NSCopying和NSMutableCopy的協議,也就是說咱們能夠調用它的copy和mutableCopy來獲得不可變和可變的副本,程序以下:
NSDictionary *testDict = [[NSDictionary alloc]initWithObjectsAndKeys:@"hello", @"test",nil];
NSDictionary *destDict = [testDict copy];
NSLog(@"test Dict:%p,retain Count: %d\ndest Dict:%p, retain Count: %d",testDict,[testDict retainCount],destDict,[destDict retainCount]);
這個在我機器上的運行結果爲:
test Dict:0x11f220, retain Count: 2
dest Dict:0x11f220,retain Count: 2
看 起來,兩個dict指向了同一片內存區域,可是retainCount加了1。這點須要理解一下,由於咱們使用NSCopying方法要返回一個不可變對 象。並且原來的testDict也是不可變的,那麼這裏的「副本」也就沒多大意義了(這就如同使用字符串常量時,系統會爲咱們優化,聲明瞭多個字符串,但 是都是常量,且內容相等,那麼系統就只爲咱們申請一塊空間,這個道理是同樣的)。既然都不可變,那麼指向同一個空間就能夠了。這裏的copy和 retain沒什麼區別。
咱們使用copyWithZone的方法返回immutable的對象,而無論原來的是可變的或是不可變的。咱們再看一下以下代碼:
NSMutableDictionary *testDict = [[NSMutableDictionary alloc]initWithObjectsAndKeys:@"hello", @"test",nil];
NSMutableDictionary *destDict = [testDict copy];
NSLog(@"test Dict:%p,retain count:%d\ndest Dict:%p,retain count:%d",testDict,[testDict retainCount],destDict,[destDict retainCount]);
[destDict setObject:@"what" forKey:@"test2"];
NSMutableDictionary是可變的,該代碼在我機器上運行的結果爲:
test Dict:0x20dcc0,retain count:1
dest Dict:0x209120,retain count:1
*** -[NSCFDictionary setObject:forKey:]: mutating method sent to immutable object
可 以看到由於咱們調用了可變對象的copy方法,這個不像以前的例子中同樣,只是retain了一下。這裏的test dict和dest Dict已是兩個對象了,可是,copyWithZone的方法返回的是不可變的對象,所以以後的setObject: forKey:方法會出現錯誤。
下面這樣改一下就OK了。
NSMutableDictionary *testDict = [[NSMutableDictionary alloc]initWithObjectsAndKeys:@"hello", @"test",nil];
NSMutableDictionary *destDict = [testDict mutableCopy];
NSLog(@"test Dict:%p,retain count:%d\ndest Dict:%p,retain count:%d",testDict,[testDict retainCount],destDict,[destDict retainCount]);
[destDict setObject:@"what" forKey:@"test2"];
NSLog(@"destDict:%@",destDict);
運行結果爲:
test Dict:0x123550,retain count:1
dest Dict:0x10a460,retain count:1
destDict:{
test = hello;
test2 = what;
由於咱們使用了mutableCopy來獲得了一個可變副本。
Note:對於系統提供的全部既支持NSCopying,又支持NSMutableCopying的類。
copy方法,獲得的是不可變對象,無論之前的是可變仍是不可變。
mutableCopy方法,獲得的是可變對象,無論之前的是可變仍是不可變。ide