strong、copy和mutableCopy详解
寫在前面
關(guān)于copy、mutableCopy和strong,一直想好好整理整理,但是因?yàn)楦鞣N原因擱置了。今天上班時(shí)發(fā)現(xiàn)老代碼中因?yàn)檫@個(gè)問題出現(xiàn)了一個(gè)特別惡心的大坑,讓我下定決心寫下這篇博文。如果你認(rèn)為自己沒掌握copy相關(guān)知識(shí),建議看看此文,這里會(huì)有你需要的;如果你認(rèn)為自己掌握了copy相關(guān)知識(shí),也建議看看此文,也許這里有你感興趣的東西。
首先和大家回顧一下有關(guān)copy的兩個(gè)概念——淺拷貝(Shallow Copy)和深拷貝(Deep Copy)。
淺拷貝:只拷貝指針,不拷貝內(nèi)容(不用為內(nèi)容分配新的內(nèi)存空間);
深拷貝:同時(shí)拷貝指針和內(nèi)容,即分配一塊新的內(nèi)存,將原內(nèi)容復(fù)制一份到該內(nèi)存中,并生成指向該內(nèi)存的新指針。(這里謝謝@燦爛天空的提醒,原來的解釋可能不太準(zhǔn)確,特此修正)
通俗點(diǎn)解釋就是,已知一條路,通往羅馬,淺拷貝就是新建一條通往這個(gè)羅馬的路。而深拷貝則是選一個(gè)地方,按照原來的羅馬新建一個(gè)(羅馬內(nèi)部包含的所有東西全部照舊的新建,比如家居、文化等等),并修一條通往新羅馬的路,這樣原來羅馬怎么樣就跟新羅馬沒關(guān)系了。
In the case of collection objects, a shallow copy means that a new collection object is created, but the contents of the original collection are not duplicated—only the object references are copied to the new container.
A deep copy duplicates the compound object as well as the contents of all of its contained objects.
四種copy情況
不可變非集合類對(duì)象的copy
直接上代碼:
NSString *immutableStr = @"KFA_test1"; NSString *copyImmutableStr = [immutableStr copy]; NSString *mutableCopyImmutableStr = [immutableStr mutableCopy]; NSLog(@"\\nimmutableStr:%p-%p\\ncopyImmutableStr:%p-%p\\nmutableImmutableStr:%p-%p",immutableStr,&immutableStr,copyImmutableStr,©ImmutableStr,mutableCopyImmutableStr,&mutableCopyImmutableStr); 復(fù)制代碼運(yùn)行結(jié)果
immutableStr:0x103426098-0x7fff5c7dba08 copyImmutableStr:0x103426098-0x7fff5c7dba00 mutableImmutableStr:0x7ffd99d34ce0-0x7fff5c7db9f8 復(fù)制代碼可以發(fā)現(xiàn),對(duì)不可變非集合類對(duì)象進(jìn)行copy時(shí),只拷貝了指針,內(nèi)容不拷貝。對(duì)不可變非集合類對(duì)象進(jìn)行mutableCopy時(shí),同時(shí)拷貝了指針和內(nèi)容。
可變非集合類對(duì)象的copy
照例上代碼:
NSMutableString *mutableStr = [@"KFA_test2" mutableCopy]; NSString *copyImmutableStr = [mutableStr copy]; NSString *mutableCopyImmutableStr = [mutableStr mutableCopy]; NSLog(@"\\nmutableStr:%p-%p\\ncopyImmutableStr:%p-%p\\nmutableImmutableStr:%p-%p",mutableStr,&mutableStr,copyImmutableStr,©ImmutableStr,mutableCopyImmutableStr,&mutableCopyImmutableStr); 復(fù)制代碼運(yùn)行結(jié)果:
mutableStr:0x7feb8bc1c940-0x7fff5610fa08 copyImmutableStr:0xa34cf2e0400c1289-0x7fff5610fa00 mutableImmutableStr:0x7feb8bc1f320-0x7fff5610f9f8 復(fù)制代碼從結(jié)果可以發(fā)現(xiàn),對(duì)可變非集合類對(duì)象進(jìn)行copy和mutableCopy,都同時(shí)拷貝指針和內(nèi)容。
不可變集合類對(duì)象的copy
這里以數(shù)組為例:
NSArray *immutableArr = @[@"KFA_test3"]; NSString *immu = immutableArr.firstObject; NSArray *copyImmutableArr = [immutableArr copy]; NSString *copyImmu = copyImmutableArr.firstObject; NSArray *mutableCopyImmutableArr = [immutableArr mutableCopy]; NSString *mutableCopyImu = mutableCopyImmutableArr.firstObject; NSLog(@"\\nimmutableArr:%p-%p\\ncopyImmutableArr:%p-%p\\nmutableCopyImmutableArr:%p-%p\\nimmu:%p-%p\\ncopyImmu:%p-%p\\nmutableCopyImu:%p-%p",immutableArr,&immutableArr,copyImmutableArr,©ImmutableArr,mutableCopyImmutableArr,&mutableCopyImmutableArr,immu,&immu,copyImmu,©Immu,mutableCopyImu,&mutableCopyImu); 復(fù)制代碼運(yùn)行結(jié)果:
immutableArr:0x7fa44a7087d0-0x7fff576619d0 copyImmutableArr:0x7fa44a7087d0-0x7fff576619c0 mutableCopyImmutableArr:0x7fa44a703370-0x7fff576619b0 immu:0x1085a0118-0x7fff576619c8 copyImmu:0x1085a0118-0x7fff576619b8 mutableCopyImu:0x1085a0118-0x7fff576619a8 復(fù)制代碼不難發(fā)現(xiàn),對(duì)不可變集合類對(duì)象進(jìn)行copy時(shí),只拷貝指針,不拷貝內(nèi)容。對(duì)不可變集合類對(duì)象進(jìn)行mutableCopy時(shí),同時(shí)拷貝指針和內(nèi)容,但是這里的內(nèi)容只針對(duì)集合對(duì)象本身,對(duì)于集合內(nèi)的元素只拷貝指針,不拷貝內(nèi)容。
可變集合類對(duì)象的copy
還是以數(shù)組為例:
NSArray *mutableArr = [@[@"KFA_test3"] mutableCopy]; NSString *mu = mutableArr.firstObject; NSArray *copyImmutableArr = [mutableArr copy]; NSString *copyImmu = copyImmutableArr.firstObject; NSArray *mutableCopyImmutableArr = [mutableArr mutableCopy]; NSString *mutableCopyImu = mutableCopyImmutableArr.firstObject; NSLog(@"\\nmutableArr:%p-%p\\ncopyImmutableArr:%p-%p\\nmutableCopyImmutableArr:%p-%p\\nmu:%p-%p\\ncopyImmu:%p-%p\\nmutableCopyImu:%p-%p",mutableArr,&mutableArr,copyImmutableArr,©ImmutableArr,mutableCopyImmutableArr,&mutableCopyImmutableArr,mu,&mu,copyImmu,©Immu,mutableCopyImu,&mutableCopyImu); 復(fù)制代碼運(yùn)行結(jié)果:
mutableArr:0x7feacbd0d250-0x7fff521409d0 copyImmutableArr:0x7feacbd06ef0-0x7fff521409c0 mutableCopyImmutableArr:0x7feacbd0c2a0-0x7fff521409b0 mu:0x10dac1118-0x7fff521409c8 copyImmu:0x10dac1118-0x7fff521409b8 mutableCopyImu:0x10dac1118-0x7fff521409a8 復(fù)制代碼我們發(fā)現(xiàn),對(duì)可變集合類對(duì)象進(jìn)行copy和mutableCopy時(shí),都同時(shí)拷貝指針和內(nèi)容。這里的內(nèi)容跟上文一樣只針對(duì)集合對(duì)象本身,對(duì)于集合內(nèi)部的元素只拷貝指針,不拷貝內(nèi)容。
strong和copy
strong和copy下的字符串
先添加兩個(gè)字符串ivar,分別用strong和copy修飾:
@property (nonatomic, strong) NSString *testStrongStr; @property (nonatomic, copy) NSString *testCopyStr; 復(fù)制代碼然后寫個(gè)測(cè)試方法:
- (void)testCopyAndStrongForString {NSString *immutableStr = @"KFA_test4";self.testStrongStr = immutableStr;self.testCopyStr = immutableStr;NSLog(@"\\nimmutableStr:%@\\nself.testStrongStr:%@\\nself.testCopyStr:%@",immutableStr,self.testStrongStr,self.testCopyStr);immutableStr = @"KFA_test4_change";NSLog(@"\\nimmutableStr:%@\\nself.testStrongStr:%@\\nself.testCopyStr:%@",immutableStr,self.testStrongStr,self.testCopyStr);NSMutableString *mutableStr = [@"KFA_test5" mutableCopy];self.testStrongStr = mutableStr;self.testCopyStr = mutableStr;NSLog(@"\\nmutableStr:%@\\nself.testStrongStr:%@\\nself.testCopyStr:%@",mutableStr,self.testStrongStr,self.testCopyStr);[mutableStr appendString:@"_change"];NSLog(@"\\nmutableStr:%@\\nself.testStrongStr:%@\\nself.testCopyStr:%@",mutableStr,self.testStrongStr,self.testCopyStr); } 復(fù)制代碼運(yùn)行結(jié)果:
immutableStr:KFA_test4 self.testStrongStr:KFA_test4 self.testCopyStr:KFA_test4 immutableStr:KFA_test4_change self.testStrongStr:KFA_test4 self.testCopyStr:KFA_test4 mutableStr:KFA_test5 self.testStrongStr:KFA_test5 self.testCopyStr:KFA_test5 mutableStr:KFA_test5_change self.testStrongStr:KFA_test5_change self.testCopyStr:KFA_test5 復(fù)制代碼從結(jié)果可以看出,當(dāng)賦值對(duì)象是不可變的字符串時(shí),strong和copy的效果一樣,因?yàn)楫?dāng)賦值對(duì)象為不可變的字符串時(shí),copy修飾的ivar進(jìn)行拷貝相當(dāng)于上文中的對(duì)不可變對(duì)象進(jìn)行copy,只拷貝指針,不考慮內(nèi)容。但是當(dāng)賦值對(duì)象為可變的字符串時(shí),當(dāng)賦值對(duì)象發(fā)生變化時(shí),strong修飾的ivar會(huì)跟著變,copy修飾的ivar進(jìn)行了拷貝(參考上文中對(duì)可變對(duì)象進(jìn)行copy),所以不會(huì)變。這里可能有同學(xué)會(huì)有疑惑,為什么明明KFA_test4變成了KFA_test4_change,內(nèi)容變了,而self.testStrongStr和self.testCopyStr的內(nèi)容沒變,這是因?yàn)镵FA_test4變成KFA_test4_change只是因?yàn)閕mmutableStr指針進(jìn)行了重指向指向了KFA_test4_change,而內(nèi)容KFA_test4屬于常量,存在棧區(qū),本身并沒有變。所以在修飾字符串時(shí)為了數(shù)據(jù)的安全,建議用copy,而不用strong。
strong和copy下的數(shù)組
同樣,先添加兩個(gè)數(shù)組ivar,分別用strong和copy修飾:
@property (nonatomic, strong) NSArray *testStrongArr; @property (nonatomic, copy) NSArray *testCopyArr; 復(fù)制代碼另外再創(chuàng)建一個(gè)model類,有一個(gè)number屬性:
@interface KFACopyModel : NSObject@property (nonatomic, copy) NSString *number;@end 復(fù)制代碼再寫兩個(gè)函數(shù)(等會(huì)測(cè)試方法里會(huì)用到),用來同時(shí)打印三個(gè)數(shù)組(里面的元素):
void KFALog(NSArray *array1, NSArray *array2, NSArray *array3) {NSString *string1 = KFANSStringFromArray(array1);NSString *string2 = KFANSStringFromArray(array2);NSString *string3 = KFANSStringFromArray(array3);NSLog(@"\\n\\%@\\n%@\\n%@",string1,string2,string3); }NSString *KFANSStringFromArray(NSArray *array) {NSMutableString *string = nil;for (NSString *objc in array) {NSString *objcStr = nil;if ([objc isKindOfClass:[NSString class]]) {objcStr = (NSString *)objc;}else if ([objc isKindOfClass:[KFACopyModel class]]) {KFACopyModel *model = (KFACopyModel *)objc;objcStr = model.number;}if (string == nil) {string = [objcStr mutableCopy];}else {[string appendFormat:@"_%@",objcStr];}}return string; } 復(fù)制代碼然后就是測(cè)試方法了:
- (void)testCopyAndStrongForArray {NSArray *immutableArr = @[@"KFA_test6"];self.testStrongArr = immutableArr;self.testCopyArr = immutableArr;KFALog(immutableArr,self.testStrongArr,self.testCopyArr);immutableArr = @[@"KFA_test6_change"];KFALog(immutableArr,self.testStrongArr,self.testCopyArr);NSMutableArray *mutableArr = [@[@"KFA_test7"] mutableCopy];self.testStrongArr = mutableArr;self.testCopyArr = mutableArr;KFALog(mutableArr, self.testStrongArr, self.testCopyArr);[mutableArr replaceObjectAtIndex:0 withObject:@"KFA_test7_change"];KFALog(mutableArr, self.testStrongArr, self.testCopyArr);KFACopyModel *model1 = [[KFACopyModel alloc] init];model1.number = @"13789892310";NSArray *immutableArr_ = @[model1];self.testStrongArr = immutableArr_;self.testCopyArr = immutableArr_;KFALog(immutableArr_, self.testStrongArr, self.testCopyArr);model1.number = @"137****2310";KFALog(immutableArr_, self.testStrongArr, self.testCopyArr);KFACopyModel *model2 = [[KFACopyModel alloc] init];model2.number = @"13982650942";NSArray *mutableArr_ = @[model2];self.testStrongArr = mutableArr_;self.testCopyArr = mutableArr_;KFALog(mutableArr_, self.testStrongArr, self.testCopyArr);model2.number = @"139****0942";KFALog(mutableArr_, self.testStrongArr, self.testCopyArr); } 復(fù)制代碼運(yùn)行結(jié)果:
KFA_test6 KFA_test6_change KFA_test6 KFA_test6 KFA_test7 KFA_test7 KFA_test7 KFA_test7_change KFA_test7_change KFA_test7 13789892310 13789892310 13789892310 137****2310 137****2310 137****2310 13982650942 13982650942 13982650942 139****0942 139****0942 139****0942 復(fù)制代碼我們發(fā)現(xiàn)一個(gè)有意思的問題,當(dāng)數(shù)組里元素為字符串時(shí),結(jié)果跟咱們預(yù)料的一樣的,但是當(dāng)數(shù)組的元素為KFACopyModel對(duì)象時(shí),結(jié)果跟咱們預(yù)料有點(diǎn)出入了。這里就得回到上文說的四種情況了。對(duì)集合類對(duì)象進(jìn)行copy和mutableCopy時(shí),即便拷貝內(nèi)容,拷貝的也是集合對(duì)象本身,對(duì)于集合內(nèi)的元素只拷貝了指針,不拷貝內(nèi)容。所以當(dāng)數(shù)組里的元素KFACopyModel對(duì)象的number屬性發(fā)生變化時(shí),拷貝的數(shù)組里元素也會(huì)跟著變化。
寫在最后
通過上文我們可以總結(jié)兩點(diǎn):
- 對(duì)于凡是具有可變子類的類,如NSString有子類NSMutableString,NSArray有子類NSMutableArray,都用copy進(jìn)行修飾,不用strong。但是對(duì)可變類,如NSMutableArray,應(yīng)該用strong,不用copy,不然就會(huì)造成明明定義一個(gè)可變數(shù)組卻不能調(diào)用可變數(shù)組的方法的后果。
- 給數(shù)組ivar賦值時(shí),為了數(shù)組的安全,最好用一個(gè)臨時(shí)的數(shù)組對(duì)象(此對(duì)象不會(huì)在其他地方用到),更不能用一個(gè)數(shù)組ivar來給另一個(gè)數(shù)組ivar賦值。
本文用的代碼全在這里,可以點(diǎn)擊下載。歡迎拍磚和轉(zhuǎn)發(fā)。
總結(jié)
以上是生活随笔為你收集整理的strong、copy和mutableCopy详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AFNetworking 3.0 发送s
- 下一篇: Android控件之Constraint