NSObject中的類方法copyWithZone問題

tags:    時間:2013-12-03 18:40:30
一般情況下,都是copy和copyWithZone函數都是從NSCoping協議中繼承下來的,但是為什麼NSObject中有一個靜態方法copyWithZone呢,這個方法的作用是什麼呢?
------解決方案--------------------
加了20分,希望有高手進來解決。。
------解決方案--------------------
本人也對此問題期待一個滿意的解答。
------解決方案--------------------
引用
本人也對此問題期待一個滿意的解答。

奇怪,為什麼問的兩個問題沒有一個人回答呢。。
------解決方案--------------------
馬一記~
------解決方案--------------------
看文檔里的解釋:
This method exists so class objects can be used in situations where you need an object that conforms to the NSCopying protocol. For example, this method lets you use a class object as a key to an NSDictionary object. You should not override this method.
簡單地說,是為了讓"類"對象也符合NSCopying協議
------解決方案--------------------
引用
看文檔里的解釋:
This method exists so class objects can be used in situations where you need an object that conforms to the NSCopying protocol. For example, this method lets you use a class object as a key to an NSDictionary object. You should not override this method.
簡單地說,是為了讓"類"對象也符合NSCopying協議

但是類對象需要這個幹嘛呢,類對象又複製不了什麼東西。
------解決方案--------------------
From: http://oasku.com/?q-104.html
 
首 先,從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 
{
    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方法,得到的是可變對象,不管以前的是可變還是不可變。
 
------解決方案--------------------
貌似跟深層複製與淺層複製有關。我也沒有怎麼弄懂。。。貼上一片覺得不錯的。。你看看理解一下。。。理解了再給我講講。。
------解決方案--------------------
引用
Quote

看文檔里的解釋:
This method exists so class objects can be used in situations where you need an object that conforms to the NSCopying protocol. For example, this method lets you use a class object as a key to an NSDictionary object. You should not override this method.
簡單地說,是為了讓"類"對象也符合NSCopying協議

但是類對象需要這個幹嘛呢,類對象又複製不了什麼東西。

上面已經說的很清楚了嘛,因為字典中的key對象需要遵循NSCopying協議,所以"類"對象也必須實現NSCopying協議才能作為key使用

推薦閱讀文章

Bookmark the permalink ,來源:互聯網