shallow & Deep Copy
今天無聊,又快要離職了,來寫點心得,也是去面試時碰到的題目。
就是深層copy 和 淺copy
在Objective - C 中 像是NSDictionaryt, NSArray 等等 他們都可以實現COPY / MutableCopy 這個語法。
你按住Command 點進去會發現他們都實現了NSCopying 這個Protocol 。
也就是說~如果你在Objective - C中沒看到NSCopying的Protocol時,程式就會當掉
例如 [[UIColor redColor]copy];
在Xcode中,對於基本數據類型 int , Bool Float 等等是直接賦值 。
但是對於指標類的型態 就有深淺層拷貝的差別,跟在C++中基本差不多,指向同一對像,生成新對像。
Xcode 為了優化 對於不可變(immutable)的Object 進行COPY 預設的都是淺層拷貝。
例如
輸出結果會是first Dict:0x100115570,retain Count: 2
second Dict:0x100115570, retain Count: 2
兩個指針指向同一個地方,RetainCount +1 證明他是指向同一個對象
這也很好理解 調用copy 他會返回一個不可變的對象,原本的firstDic也是一個不可變,因為都是不可變,
所以就乾脆指向同一位址就可以了。 所以這邊的COPY和Retain是並無差別
如果把NSDictionary 改成NSMutableDictionary 的話,
|
|
輸出結果會發現RetainCount 兩個都變成 1 指向的位址也變成不同。
但是需注意的是這裡SecondDic雖然你宣告的一個NSMutableDictionary 可是copy返回的是一個不可變的,
所以如果你要對SecondDic做操作時,可能馬上就會Crash。
所以這邊NSMutableDictionary SecondDic 需改成NSDictionary secondDic 才行
或是調用Mutablecopy
這是會返回一個可變的Object 。
如果你的Class 是一個自定義的話,當你執行 copy 或 MutableCopy時一定馬上當,
你需先在@interface 加入protocol <NSCopying>
然後再m檔中
一一對你的屬性對深或淺的實現 例如下列方法
m檔中
實現 - (id)copyWithZone:(NSZone *)zone;
假如你的Class 名叫做 Family
|
|
可以NSLog一下 兩個family 和 family.name 的指針
會發現淺拷貝的變量指針是指向同一對象,也就是雖然他COPY了兩個物件,可是內容物還是指向同一塊空間
再看看深層拷貝, 就會發現family的name 兩個指針是不同的,也代表這是真正的實現了COPY這個定義
##但是
這樣是只針對常量而言,如果Array中包的是object呢?
當用了mutableCopy後,兩個array的指標是不一樣,但是數組內的內容指標還是指向同一個位址。 要如何解決呢?
有三種方法
- 用 [[]NSArray alloc]initWithArray:copyitems] 這個函數,他會copy數組內的每個元素調用copy函數,並把返回的id加入到新的數組中。
- 用archiver方式,NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyArchiver archivedDataWithRootObject:oldArr]];
- 用category 實現自定義的copy
##結論
在Foundation中,當我們COPY是一個immutable(不可變)對象時,它的作用相當於retain (Xcode 優化)
當使用mutableCopy時,不管源本對像是可變或不可變,都會回傳一個可變的而且實行真正的Copy
當copy一個可變對像時,返回是一個不可變的,但也同樣實現了真正的Copy