strong、copy和mutableCopy详解
寫在前面
關于copy、mutableCopy和strong,一直想好好整理整理,但是因為各種原因擱置了。今天上班時發現老代碼中因為這個問題出現了一個特別惡心的大坑,讓我下定決心寫下這篇博文。如果你認為自己沒掌握copy相關知識,建議看看此文,這里會有你需要的;如果你認為自己掌握了copy相關知識,也建議看看此文,也許這里有你感興趣的東西。
首先和大家回顧一下有關copy的兩個概念——淺拷貝(Shallow Copy)和深拷貝(Deep Copy)。
淺拷貝:只拷貝指針,不拷貝內容(不用為內容分配新的內存空間);
深拷貝:同時拷貝指針和內容,即分配一塊新的內存,將原內容復制一份到該內存中,并生成指向該內存的新指針。(這里謝謝@燦爛天空的提醒,原來的解釋可能不太準確,特此修正)
通俗點解釋就是,已知一條路,通往羅馬,淺拷貝就是新建一條通往這個羅馬的路。而深拷貝則是選一個地方,按照原來的羅馬新建一個(羅馬內部包含的所有東西全部照舊的新建,比如家居、文化等等),并修一條通往新羅馬的路,這樣原來羅馬怎么樣就跟新羅馬沒關系了。
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情況
不可變非集合類對象的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); 復制代碼運行結果
immutableStr:0x103426098-0x7fff5c7dba08 copyImmutableStr:0x103426098-0x7fff5c7dba00 mutableImmutableStr:0x7ffd99d34ce0-0x7fff5c7db9f8 復制代碼可以發現,對不可變非集合類對象進行copy時,只拷貝了指針,內容不拷貝。對不可變非集合類對象進行mutableCopy時,同時拷貝了指針和內容。
可變非集合類對象的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); 復制代碼運行結果:
mutableStr:0x7feb8bc1c940-0x7fff5610fa08 copyImmutableStr:0xa34cf2e0400c1289-0x7fff5610fa00 mutableImmutableStr:0x7feb8bc1f320-0x7fff5610f9f8 復制代碼從結果可以發現,對可變非集合類對象進行copy和mutableCopy,都同時拷貝指針和內容。
不可變集合類對象的copy
這里以數組為例:
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); 復制代碼運行結果:
immutableArr:0x7fa44a7087d0-0x7fff576619d0 copyImmutableArr:0x7fa44a7087d0-0x7fff576619c0 mutableCopyImmutableArr:0x7fa44a703370-0x7fff576619b0 immu:0x1085a0118-0x7fff576619c8 copyImmu:0x1085a0118-0x7fff576619b8 mutableCopyImu:0x1085a0118-0x7fff576619a8 復制代碼不難發現,對不可變集合類對象進行copy時,只拷貝指針,不拷貝內容。對不可變集合類對象進行mutableCopy時,同時拷貝指針和內容,但是這里的內容只針對集合對象本身,對于集合內的元素只拷貝指針,不拷貝內容。
可變集合類對象的copy
還是以數組為例:
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); 復制代碼運行結果:
mutableArr:0x7feacbd0d250-0x7fff521409d0 copyImmutableArr:0x7feacbd06ef0-0x7fff521409c0 mutableCopyImmutableArr:0x7feacbd0c2a0-0x7fff521409b0 mu:0x10dac1118-0x7fff521409c8 copyImmu:0x10dac1118-0x7fff521409b8 mutableCopyImu:0x10dac1118-0x7fff521409a8 復制代碼我們發現,對可變集合類對象進行copy和mutableCopy時,都同時拷貝指針和內容。這里的內容跟上文一樣只針對集合對象本身,對于集合內部的元素只拷貝指針,不拷貝內容。
strong和copy
strong和copy下的字符串
先添加兩個字符串ivar,分別用strong和copy修飾:
@property (nonatomic, strong) NSString *testStrongStr; @property (nonatomic, copy) NSString *testCopyStr; 復制代碼然后寫個測試方法:
- (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); } 復制代碼運行結果:
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 復制代碼從結果可以看出,當賦值對象是不可變的字符串時,strong和copy的效果一樣,因為當賦值對象為不可變的字符串時,copy修飾的ivar進行拷貝相當于上文中的對不可變對象進行copy,只拷貝指針,不考慮內容。但是當賦值對象為可變的字符串時,當賦值對象發生變化時,strong修飾的ivar會跟著變,copy修飾的ivar進行了拷貝(參考上文中對可變對象進行copy),所以不會變。這里可能有同學會有疑惑,為什么明明KFA_test4變成了KFA_test4_change,內容變了,而self.testStrongStr和self.testCopyStr的內容沒變,這是因為KFA_test4變成KFA_test4_change只是因為immutableStr指針進行了重指向指向了KFA_test4_change,而內容KFA_test4屬于常量,存在棧區,本身并沒有變。所以在修飾字符串時為了數據的安全,建議用copy,而不用strong。
strong和copy下的數組
同樣,先添加兩個數組ivar,分別用strong和copy修飾:
@property (nonatomic, strong) NSArray *testStrongArr; @property (nonatomic, copy) NSArray *testCopyArr; 復制代碼另外再創建一個model類,有一個number屬性:
@interface KFACopyModel : NSObject@property (nonatomic, copy) NSString *number;@end 復制代碼再寫兩個函數(等會測試方法里會用到),用來同時打印三個數組(里面的元素):
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; } 復制代碼然后就是測試方法了:
- (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); } 復制代碼運行結果:
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 復制代碼我們發現一個有意思的問題,當數組里元素為字符串時,結果跟咱們預料的一樣的,但是當數組的元素為KFACopyModel對象時,結果跟咱們預料有點出入了。這里就得回到上文說的四種情況了。對集合類對象進行copy和mutableCopy時,即便拷貝內容,拷貝的也是集合對象本身,對于集合內的元素只拷貝了指針,不拷貝內容。所以當數組里的元素KFACopyModel對象的number屬性發生變化時,拷貝的數組里元素也會跟著變化。
寫在最后
通過上文我們可以總結兩點:
- 對于凡是具有可變子類的類,如NSString有子類NSMutableString,NSArray有子類NSMutableArray,都用copy進行修飾,不用strong。但是對可變類,如NSMutableArray,應該用strong,不用copy,不然就會造成明明定義一個可變數組卻不能調用可變數組的方法的后果。
- 給數組ivar賦值時,為了數組的安全,最好用一個臨時的數組對象(此對象不會在其他地方用到),更不能用一個數組ivar來給另一個數組ivar賦值。
本文用的代碼全在這里,可以點擊下載。歡迎拍磚和轉發。
總結
以上是生活随笔為你收集整理的strong、copy和mutableCopy详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AFNetworking 3.0 发送s
- 下一篇: Android控件之Constraint