OC中關(guān)于copy和mutableCopy

?這里主要有兩個(gè)概念深拷貝和淺拷貝,主要涉及兩個(gè)方法 - (id)copy;- (id)mutableCopy; 和一個(gè)屬性修飾關(guān)鍵字@copy。

?深拷貝 :即內(nèi)容拷貝,指拷貝對(duì)象的具體內(nèi)容,而內(nèi)存地址是重新分配的,兩個(gè)對(duì)象雖然存的值是相同的,但是內(nèi)存地址不一樣,兩個(gè)對(duì)象也互不影響,互不干涉(對(duì)于集合類這里有點(diǎn)特殊,下面會(huì)詳細(xì)講述)。

?淺拷貝:即指針拷貝,是對(duì)內(nèi)存地址的復(fù)制,讓目標(biāo)對(duì)象指針和源對(duì)象指向同一片內(nèi)存空間。

一、對(duì)象可以實(shí)現(xiàn)拷貝的前提

?在OC中,若要使對(duì)象可以擁有可拷貝操作,則該對(duì)象所屬的類必須遵守NSCopyingNSMutableCopy協(xié)議,并重寫copyWithZone:mutableCopyWithZone:方法。
?對(duì)于自定義類一般只實(shí)現(xiàn)NSCopying協(xié)議,同時(shí)如果遵循了NSCopying協(xié)議同時(shí)實(shí)現(xiàn)了copyWithZone:方法,則實(shí)例對(duì)象調(diào)用copy方法([animal copy])時(shí)會(huì)生成一個(gè)新的對(duì)象,值得注意的是這是一個(gè)深拷貝。

  • 若自定義類直接繼承于NSObject類,則不需要[super copyWithZone:zone] ;
  • 若自定義類的父類或者超類繼承與NSObject,且其父類或超類均未遵循NSCopying協(xié)議,則不需要[super copyWithZone:zone] ;
  • 若自定義類的父類或者超類繼承與NSObject,且其父類或超類遵循NSCopying協(xié)議,則需要[super copyWithZone:zone] ;
@interface Animal : NSObject
@property (nonatomic,copy) NSString * name;
@property (nonatomic,copy) NSString * age;
@end

@interface Animal()<NSCopying>

@end
// --------------------------------------------------------
@implementation Animal

- (id)copyWithZone:(NSZone *)zone
{
    Animal * animal = [[Animal allocWithZone:zone] init];
    animal.name = self.name;
    animal.age = self.age;
    return animal;
}
// -----------------------------------------------------
//在其他地方調(diào)用
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    Animal * animal = [[Animal alloc] init];
    animal.name = @"男兒何惜死";
    animal.age = @"27";
    Animal * copyAnimal = [animal copy];
    NSLog(@"animal.name/age = %@/%@, copyAnimal.name/age = %@/%@", animal.name, animal.age, copyAnimal.name, copyAnimal.age);
    NSLog(@"animal.address = %p, copyAnimal.address = %p", animal, copyAnimal);  
}
@end
二、Copy和MutableCopy的區(qū)別

? 總的來說:如果不可變對(duì)象進(jìn)行復(fù)制,copy是指針復(fù)制(淺拷貝)和mutableCopy就是對(duì)象復(fù)制(深拷貝)。如果是對(duì)可變對(duì)象復(fù)制,都是深拷貝。Copy 返回一個(gè)不可變對(duì)象的副本,MutalbeCopy返回一個(gè)可變對(duì)象的副本。至于深拷貝到什么地步,下面針對(duì)不同情況一一列舉:

1、NSString、NSMutableString:
  • 普通字符串NSString/NSMutableString使用copy與mutableCopy的區(qū)別:
NSString * string = @"男兒何惜死";
NSString * copyString = [string copy];
NSMutableString * mutableCopyStr = [string mutableCopy];
NSLog(@"string = %p, copyStr = %p mutableCopyStr = %p", string, copyString, mutableCopyStr);

NSMutableString * mutableString = [NSMutableString stringWithString:@"破膽與君嘗"];
NSString * copyMutableStr = [mutableString copy];
NSMutableString * mutableCopyMutStr = [mutableString mutableCopy];
NSLog(@"mutableString = %p, copyMutableStr = %p mutableCopyMutStr = %p", mutableString, copyMutableStr, mutableCopyMutStr);

// ------------------------------------------------
// 打印結(jié)果:驗(yàn)證了總結(jié)的結(jié)論
string = 0x10e1cf098, copyStr = 0x10e1cf098 mutableCopyStr = 0x600000442520
mutableString = 0x600000442550, copyMutableStr = 0x600000442580 mutableCopyMutStr = 0x6000004425b0
  • 對(duì)于NSString類來說當(dāng)作為屬性時(shí),關(guān)鍵字@copy與@strong的區(qū)別:
    • 當(dāng)賦予屬性值為不可變字符串時(shí),使用@copy和@strong關(guān)鍵字是沒有區(qū)別的;
    • 當(dāng)賦予屬性值為可變字符串時(shí),需要特別謹(jǐn)慎使用@copy和@strong;因?yàn)槭褂聾strong修飾后屬性指針與源指針指向同一塊內(nèi)存區(qū)域,當(dāng)該內(nèi)存中數(shù)據(jù)發(fā)生變化屬性值也會(huì)變化,這可能不是我們想要看到的。
@property (nonatomic,copy) NSString * name;
@property (nonatomic,strong) NSString * strongName;
// ----------------------------------------------------
NSMutableString * mutableString = [NSMutableString stringWithString:@"男兒何惜死"];
self.name = mutableString;
self.strongName = mutableString;
NSLog(@"mutableString = %p, self.name = %p self.strongName = %p", mutableString, self.name, self.strongName);
NSLog(@"mutableString = %@, self.name = %@ self.strongName = %@", mutableString, self.name, self.strongName);
[mutableString appendString:@",破膽與君嘗"];
NSLog(@"mutableString = %@, self.name = %@ self.strongName = %@", mutableString, self.name, self.strongName);

// ----------------------------------------------------
mutableString = 0x600000241a40, self.name = 0x600000241a70 self.strongName = 0x600000241a40
mutableString = 男兒何惜死, self.name = 男兒何惜死 self.strongName = 男兒何惜死
mutableString = 男兒何惜死,破膽與君嘗, self.name = 男兒何惜死 self.strongName = 男兒何惜死,破膽與君嘗
2、NSArray/NSMutableArray、NSDictionary/NSMutableDictionary、NSSet/NSMutableSet等集合類
(1、)對(duì)于常見使用@strong修飾的不可變集合:
@property (nonatomic,strong) NSArray * testArray;
// -------------------------------------------------------
// 示例一:賦予全部是不可變?cè)氐牟豢勺兗?self.testArray = @[@"不負(fù)光陰", @"不負(fù)卿"];
NSArray * test_1 = [self.testArray copy];
NSMutableArray * test_2 = [self.testArray mutableCopy];
NSLog(@"self.testArray = %p, test_1 = %p, test_2 = %p", self.testArray, test_1, test_2);
NSLog(@"\n self.testArray.firstObject = %p, self.testArray.lastObject = %p;
\n test_1.firstObject = %p, test_1.lastObject = %p;\n test_2.firstObject = %p, test_2.lastObject = %p",
 self.testArray[0], self.testArray[1], test_1[0], test_1[1], test_2[0], test_2[1]);
// ------------------------------------------------------
self.testArray = 0x604000039f60, test_1 = 0x604000039f60, test_2 = 0x60400025d130
self.testArray.firstObject = 0x10f61d0a8, self.testArray.lastObject = 0x10f61d0c8;
test_1.firstObject = 0x10f61d0a8, test_1.lastObject = 0x10f61d0c8;
test_2.firstObject = 0x10f61d0a8, test_2.lastObject = 0x10f61d0c8
// ---------------------------------------------------------
// 由上可知,雖然mutableCopy是深拷貝,但是不徹底,test_2中的元素與源數(shù)組元素依然指向同一塊內(nèi)存區(qū)域。
// 這在數(shù)組元素存在可變?cè)貢r(shí)會(huì)發(fā)生奇妙的事情。

// 示例二:賦予含有可變?cè)氐牟豢勺兗?self.testArray = @[[NSMutableString stringWithString:@"不負(fù)光陰"], @"不負(fù)卿"];
NSArray * test_1 = [self.testArray copy];
NSMutableArray * test_2 = [self.testArray mutableCopy];
NSLog(@"self.testArray = %@, test_1 = %@, test_2 = %@", self.testArray, test_1, test_2);
NSLog(@"self.testArray = %p, test_1 = %p, test_2 = %p", self.testArray, test_1, test_2);
NSLog(@"\n self.testArray.firstObject = %p, self.testArray.lastObject = %p;
\n test_1.firstObject = %p, test_1.lastObject = %p;\n test_2.firstObject = %p, test_2.lastObject = %p",
 self.testArray[0], self.testArray[1], test_1[0], test_1[1], test_2[0], test_2[1]);
NSMutableString * firstStr = [self.testArray firstObject];
[firstStr appendString:@",不負(fù)自己"];
NSLog(@"self.testArray = %@, test_1 = %@, test_2 = %@", self.testArray, test_1, test_2);
// --------------------------------------------------------
self.testArray =    (
    "不負(fù)光陰",
    "不負(fù)卿",
),
 test_1 =   (
    "不負(fù)光陰",
    "不負(fù)卿",
), 
test_2 =    (
    "不負(fù)光陰",
    "不負(fù)卿",
)

self.testArray = 0x60400022bf80, test_1 = 0x60400022bf80, test_2 = 0x604000445100
self.testArray.firstObject = 0x604000442430, self.testArray.lastObject = 0x1030c8df8;
 test_1.firstObject = 0x604000442430, test_1.lastObject = 0x1030c8df8;
 test_2.firstObject = 0x604000442430, test_2.lastObject = 0x1030c8df8

self.testArray =    (
    "不負(fù)光陰,不負(fù)自己",
    "不負(fù)卿",
), 
test_1 =    (
    "不負(fù)光陰,不負(fù)自己",
    "不負(fù)卿",
), 
test_2 =    (
    "不負(fù)光陰,不負(fù)自己",
    "不負(fù)卿",
)

// --------------------------------------------------------
// 可以看到所有數(shù)組的第一個(gè)元素全部發(fā)生了改變。同樣數(shù)組內(nèi)即使存在可變數(shù)元素,外部使用mutableCopy,內(nèi)部元素依然指向同一塊內(nèi)存區(qū)域!
// 試想一下如果 將[firstStr appendString:@",不負(fù)自己"];
// 改為 firstStr = [NSMutableString stringWithString:@"不負(fù)光陰,不負(fù)自己"];
//打印結(jié)果如何?為什么?

// 示例三:賦予可變集合(這里與NSString相似)
NSMutableArray * baseArray = [NSMutableArray arrayWithArray:@[@"不負(fù)光陰", @"不負(fù)卿"]];
self.testArray = baseArray;
NSArray * test_1 = [self.testArray copy];
NSMutableArray * test_2 = [self.testArray mutableCopy];
NSLog(@"baseArray = %p, self.testArray = %p, test_1 = %p, test_2 = %p", baseArray, self.testArray, test_1, test_2);
NSLog(@"baseArray = %@, self.testArray = %@", baseArray, self.testArray);
[baseArray addObject:@"不負(fù)自己"];
NSLog(@"baseArray = %@, self.testArray = %@", baseArray, self.testArray);
// ----------------------------------------------------------

baseArray = 0x6000004442f0, self.testArray = 0x6000004442f0, test_1 = 0x6000000342e0, test_2 = 0x600000444320
baseArray =     (
    "不負(fù)光陰",
    "不負(fù)卿",
), self.testArray =     (
    "不負(fù)光陰",
    "不負(fù)卿",
)
baseArray =     (
    "不負(fù)光陰",
    "不負(fù)卿",
    "不負(fù)自己",
), self.testArray =     (
    "不負(fù)光陰",
    "不負(fù)卿",
    "不負(fù)自己",
)
// ----------------------------------------------------------
// 賦值操作是一個(gè)淺拷貝的過程;
// 可以看到當(dāng)源數(shù)據(jù)發(fā)生變化時(shí)self.testArray也與之變化,這可能也不是我們想看到到~

(2、)使用@copy關(guān)鍵字修飾的不可變集合:

?當(dāng)賦予其不可變集合對(duì)象時(shí)與@strong沒有區(qū)別,對(duì)其進(jìn)行copy或者mutableCopy操作與(1、)中示例一、二情況一致,即copy操作生成的對(duì)象與原對(duì)象指向同一塊內(nèi)存區(qū)域,mutableCopy則不同;
?當(dāng)對(duì)其賦予可變集合時(shí),由于是@copy修飾,此時(shí)雖然是賦值操作同時(shí)也是一個(gè)深拷貝的過程,會(huì)在一個(gè)新的內(nèi)存地址來存儲(chǔ)@copy修飾的集合,集合內(nèi)元素的地址與源集合中元素地址依然一樣,但是此時(shí)再修改源集合,目標(biāo)集合不會(huì)再改變!

@property (nonatomic,copy) NSArray * testCopyArray;
// -------------------------------------------------------------------
NSMutableArray * baseArray = [NSMutableArray arrayWithArray:@[@"不負(fù)光陰", @"不負(fù)卿"]];
self.testCopyArray = baseArray;
NSArray * test_1 = [self.testCopyArray copy];
NSMutableArray * test_2 = [self.testCopyArray mutableCopy];
NSLog(@"baseArray = %p, self.testCopyArray = %p, test_1 = %p, test_2 = %p", baseArray, self.testCopyArray, test_1, test_2);
NSLog(@"baseArray = %@, self.testCopyArray = %@", baseArray, self.testCopyArray);
[baseArray addObject:@"不負(fù)自己"];
NSLog(@"baseArray = %@, self.testCopyArray = %@", baseArray, self.testCopyArray);
// -------------------------------------------------------------------
 baseArray = 0x60400025de80, self.testCopyArray = 0x60400022bb60, test_1 = 0x60400022bb60, test_2 = 0x60400025deb0
 baseArray =    (
    "不負(fù)光陰",
    "不負(fù)卿",
), self.testCopyArray =     (
    "不負(fù)光陰",
    "不負(fù)卿",
)
baseArray =     (
    "不負(fù)光陰",
    "不負(fù)卿",
    "不負(fù)自己",
), self.testCopyArray =     (
    "不負(fù)光陰",
    "不負(fù)卿",
)
// -------------------------------------------------------------------
// 是不是覺得使用@copy修飾的不可變集合更靠譜一些??
// 同時(shí)需要注意的是:對(duì)于不可變數(shù)組,不管用什么修飾,不管給予其賦值的是可變還是不可變的數(shù)組,copy操作生成的對(duì)象與原對(duì)象均指向同一塊內(nèi)存區(qū)域?。?!
(3、)使用@strong修飾的可變集合:
// 與(1、)中的第三種情況(示例三)相似
@property (nonatomic,strong) NSMutableArray * testArray;
 // ---------------------------------------------------------------
NSMutableArray * baseArray = [NSMutableArray arrayWithArray:@[@"不負(fù)光陰", @"不負(fù)卿"]];
self.testArray = baseArray;
NSArray * test_1 = [self.testArray copy];
NSMutableArray * test_2 = [self.testArray mutableCopy];
NSLog(@"baseArray = %p, self.testArray = %p, test_1 = %p, test_2 = %p", baseArray, self.testArray, test_1, test_2);
NSLog(@"baseArray = %@, self.testArray = %@", baseArray, self.testArray);
[baseArray addObject:@"不負(fù)自己"];
NSLog(@"baseArray = %@, self.testArray = %@", baseArray, self.testArray);
// -------------------------------------------------------------------
baseArray = 0x6000004496c0, self.testArray = 0x6000004496c0, test_1 = 0x6000000307c0, test_2 = 0x6000004496f0
baseArray =     (
    "不負(fù)光陰",
    "不負(fù)卿",
), self.testArray =     (
    "不負(fù)光陰",
    "不負(fù)卿",
)
baseArray =     (
    "不負(fù)光陰",
    "不負(fù)卿",
    "不負(fù)自己",
), self.testArray =     (
    "不負(fù)光陰",
    "不負(fù)卿",
    "不負(fù)自己",
)
(4、)使用@copy修飾的可變集合:
// 為其賦值等操作均沒啥問題,問題出在將其作為一個(gè)值,賦予其他可變集合時(shí)!
@property (nonatomic,copy) NSMutableArray * testCopyArray;
// -------------------------------------------------------------------
self.testCopyArray = [NSMutableArray arrayWithArray:@[@"不負(fù)光陰", @"不負(fù)卿"]];
NSMutableArray * baseArray = [NSMutableArray array];
baseArray = self.testCopyArray;
NSLog(@"baseArray = %p, self.testCopyArray = %p", baseArray, self.testCopyArray);
[self.testCopyArray addObject:@"不負(fù)自己"];
[baseArray addObject:@"不負(fù)自己"];
// -------------------------------------------------------------------
//此時(shí)會(huì)崩潰
baseArray = 0x60400022acc0, self.testCopyArray = 0x60400022acc0
-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x60400022acc0

// 可以看到baseArray已經(jīng)是一個(gè)不可變的集合,所以對(duì)其進(jìn)行增刪等操作會(huì)引發(fā)異常。
// 原因就是因?yàn)樾揎椩~是@copy,我們上面已經(jīng)強(qiáng)調(diào)了copy操作返回不可變對(duì)象,
// mutableCopy返回可變對(duì)象,這與是否是深拷貝與淺拷貝無關(guān),要切記。

?以上均是用數(shù)組舉例,其實(shí)字典等也相似的,可以自行嘗試一下。這里給出一個(gè)簡單的??

@property (nonatomic,strong) NSDictionary * testDic;
// ---------------------------------------------------------------------
NSMutableDictionary * dic = [NSMutableDictionary dictionaryWithDictionary:@{@"001" : @"男兒何惜死", @"002" : @"破膽與君嘗"}];
self.testDic = dic;
NSDictionary * test_1 = [self.testDic copy];
NSMutableDictionary * test_2 = [self.testDic mutableCopy];
NSLog(@"dic = %p, self.testDic = %p, test_1 = %p, test_2 = %p", dic, self.testDic, test_1, test_2);
NSLog(@"dic = %@, self.testDic = %@", dic, self.testDic);
[dic setObject:@"兩情若是久長時(shí)" forKey:@"003"];
NSLog(@"dic = %@, self.testDic = %@", dic, self.testDic);
// ---------------------------------------------------------------------
dic = 0x604000030320, self.testDic = 0x604000030320, test_1 = 0x604000030340, test_2 = 0x604000030360

dic =   {
    001 = "男兒何惜死",
    002 = "破膽與君嘗",
}, self.testDic =   {
    001 = "男兒何惜死",
    002 = "破膽與君嘗",
}

dic =   {
    001 = "男兒何惜死",
    002 = "破膽與君嘗",
    003 = "兩情若是久長時(shí)",
}, self.testDic =   {
    001 = "男兒何惜死",
    002 = "破膽與君嘗",
    003 = "兩情若是久長時(shí)",
}
三、集合的真正的深拷貝

?上面提到集合的深拷貝并不徹底,集合內(nèi)的元素的地址依然是相同的,那么如何實(shí)現(xiàn)集合的真正的拷貝呢?參考文檔

// 可以將該集合歸檔然后再解檔,前提是內(nèi)容均符合NSCoding協(xié)議,這樣可以得到一個(gè)真正意義上的深拷貝。
@property (nonatomic,strong) NSArray * testArray;
// ---------------------------------------------------------------------
self.testArray = @[@"不負(fù)光陰", @"不負(fù)卿"];
NSArray * deepArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:self.testArray]];
NSLog(@"self.testArray = %p, deepArray = %p", self.testArray, deepArray);
NSLog(@"self.testArray = %@, deepArray = %@", self.testArray, deepArray);
NSLog(@"self.testArray[0] = %p, self.testArray[1] = %p, deepArray[0] = %p, deepArray[1] = %p", self.testArray[0], self.testArray[1], deepArray[0], deepArray[1]);
// ---------------------------------------------------------------------
self.testArray = 0x604000229b60, deepArray = 0x604000229be0
self.testArray =    (
    "不負(fù)光陰",
    "不負(fù)卿",
), deepArray =  (
    "不負(fù)光陰",
    "不負(fù)卿",
)
self.testArray[0] = 0x10350d238, self.testArray[1] = 0x10350d258, deepArray[0] = 0x604000229c20, deepArray[1] = 0x604000229b80
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容