OC中的copy的實現原理與深淺拷貝

2021-06-25 10:46:49 字數 4419 閱讀 7091

首先,從copy開始說,簡而言之,copy的目的就是生成乙個新的例項,然後把其成員都按原例項賦值。對於非指標型的成員,比如bool, int, float,這樣的賦值可以直接進行。但是對於指標型的資料,比如objc中用到的物件,就有deep copy和shallow copy的區別——這個和在c++中的基本上是一樣的:是生成新的成員物件,或是指向同一成員物件。

了解了這點以後,再看看copy在 objetive-c中的實現方式。如果要呼叫乙個物件的copy方法,這個物件必須遵循nscopying的協議。這個協議中規定了乙個方法:- (id)copywithzone:(nszone *)zone;我們就是通過實現這個方法給物件提供拷貝的功能。對於很多現有類,如nsstring,nsdictionary,。。。這個方法已經實 現。假設我們現在自定義了乙個類,需要為這個類提供拷貝的功能,就需要自己來動手寫copywithzone的方法:示例如下:

這個是自定義的類:

@inte***ce product : nsobject 

@end

然後我們需要在product類中實現nscopying中的copywithzone方法:

- (id)copywithzone:(nszone *)zone

那麼這樣,如果我們有乙個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的copy方法得到了乙個delegate的副本。至於如何得到delegate的副本,就要看 delegate的copywithzone方法的實現了,不在這個層面的考慮中。也就是說,copy總是一中「遞迴」的形式,從上到下,我們可以一層一 層的考慮。

- (void)setdelegate: (id)adelegate

這樣操作後,delegate和adelegate為同一物件,但是為了記憶體管理方面的要求,我們呼叫了retain來將reference count加了一。當然,如果不需要了,還可以直接賦值(assign):

- (void)setdelegate: (id)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方法,得到的是可變物件,不管以前的是可變還是不可變。

關於python深copy與淺copy的一點理解

一直對python深copy和淺copy似懂非懂的狀態,看了這篇文章,覺得自己懂了很多,給各位參考!出處 import copy a 1 不可變資料型別 copy a copy.copy a print id a id copy a 記憶體位址相同 a 1,2 可變資料型別 copy a copy....

copy 的實現原理與深淺拷貝

首先,從copy開始說,簡而言之,copy的目的就是生成乙個新的例項,然後把其成員都按原例項賦值。對於非指標型的成員,比如bool,int,float,這樣的賦值可以直接進行。但是對於指標型的資料,比如objc中用到的物件,就有deep copy和shallow copy的區別 這個和在c 中的基本...

copy 的實現原理與深淺拷貝

首先,從copy開始說,簡而言之,copy的目的就是生成乙個新的例項,然後把其成員都按原例項賦值。對於非指標型的成員,比如bool,int,float,這樣的賦值可以直接進行。但是對於指標型的資料,比如objc中用到的物件,就有deep copy和shallow copy的區別 這個和在c 中的基本...