copy和mutablecopy 源于對數(shù)據(jù)的復制需求,對于對象類型的數(shù)據(jù)來說,區(qū)別于直接持有這個數(shù)據(jù)對象的方式,復制可以確保所復制出來的對象不受源對象數(shù)據(jù)修改的影響。
而oc中的copy對于數(shù)值類型,對象類型,及容器數(shù)據(jù)對象類型都有不同的門道
對于數(shù)值類型來說,copy意味著對數(shù)據(jù)的直接復制,而對于對象類型來說,有淺復制和深復制的區(qū)別
在詳細討論深淺復制及copy與mutableCopy之前,先來看幾個例子:
NSArray的copy和mutableCopy
以容器類NSArray及NSMutableArray為例
NSMutableArray* ma1 = [[NSMutableArray alloc] initWithObjects:@"v1",@"v2", nil];
__unused NSMutableArray* ma2 = [ma1 mutableCopy];
__unused NSArray* ca1 = [ma1 copy];
__unused NSArray* ca2 = [ca1 copy];
_ca3 = [ca1 copy]; // ca3定義為屬性 @property (copy) NSArray* ca3;
[ma1 removeObjectAtIndex:1];
__unused CFIndex i = CFGetRetainCount((__bridge CFArrayRef)ca1);
ca1與ca2及_ca3持有同一個數(shù)組,且i值為3,而最終ma1值為 {@"v1"},ca1,ca2,_ca3值為{@"v1",@"v2"}
NSString的copy
int e = 1;
NSString* oa1 = [NSString stringWithFormat:@"abc%d",e];
__unused NSString* ca1 = [oa1 copy];
__unused NSString* ca2 = [oa1 copy];
__unused CFIndex i = CFGetRetainCount((__bridge CFStringRef)oa1);
oa1,ca1,ca2都指向同一個字符串對象,但比較奇怪的是i 值為9223372036854775807,可見其引用計數(shù)非法,編譯器在編譯階段已經(jīng)生成了字符串并為其分配了空間,而如果調整成這樣
int e = arc4random();
NSString* oa1 = [NSString stringWithFormat:@"abc%d",e];
__unused NSString* ca1 = [oa1 copy];
__unused NSString* ca2 = [oa1 copy];
__unused CFIndex i = CFGetRetainCount((__bridge CFStringRef)oa1);
則i 值為4
深淺拷貝
根據(jù)object copying(apple 文檔), 支持復制的對象需要實現(xiàn)NSCopying,若有需要還可實現(xiàn)NSMutableCopying,即可以向其發(fā)送copy甚至mutableCopy消息。
而深淺復制的不同在于,雖然對于標量數(shù)據(jù)來說,深淺復制都會對其進行復制,而對于對象類型來說,深復制是完全復制出一個同樣的對象,而淺復制則只復制對象的引用,比如像前面NSArray例子中那樣只是引用計數(shù)增加1
深拷貝和淺拷貝與copy及mutableCopy
需要注意的是 深拷貝和淺拷貝與copy及mutableCopy并沒有一對一的關系,并不是說copy即是淺拷貝,mutableCopy即是深拷貝。因為copy消息返回值其實是對應的NSCopying方法 copyWithZone:的返回值,如果覆蓋這個方法,你可以僅僅簡單地返回原對象,也可以自己實現(xiàn)深拷貝,或者不算深也不算淺的拷貝,這取決于你自己的需求。
在開發(fā)中除了自己編寫的類,很多情況下是對ios系統(tǒng)類的copy與mutableCopy的使用,了解它們的一些特性也是copy與mutableCopy這個點中很重要的部分。
像前面NSArray的例子,一般來說mutableCopy會進行深拷貝,即會生成新的對象,不管原對象是否是mutable對象,而如果原對象是mutable對象,則copy也會進行深復制,其結果一定是返回一個immutable對象。
嵌套對象
所謂的深復制其實也會兩種,一種是對于嵌套的對象同樣進行深復制,另一種是對于嵌套的對象進行淺復制
NSMutableDictionary* oa1 = [@{@"k1":@"v1",@"k2":[@"v2" mutableCopy]} mutableCopy];
__unused NSDictionary* ca1 = [oa1 copy];
__unused NSDictionary* ca2 = [ca1 copy];
__unused CFIndex i = CFGetRetainCount((__bridge CFDictionaryRef)ca1);
[[oa1 objectForKey:@"k2"] appendString:@"append"];
比如這個例子中,oa1雖然與ca1及ca2并不是同一個對象,但其中的元素其實是指向的同一個對象,因為最終ca1的值為
{
k1 = v1;
k2 = v2append;
}
也就是說在容器對象這個層面上,深拷貝已經(jīng)生成了新的容器對象,但對容器對象中包含的子對象卻還只是進行了淺復制。