copy
這個英文單詞,讓我第一個想起的是copy忍者卡卡西。我的很是喜歡卡卡西,和誰對戰都是五五開的上忍。copy
翻譯成中文就是複製的意思,爲何咱們想要複製呢?我以爲緣由有下面幾點:面試
那麼回到iOS開發,其實類比到生活中也差很少。修改複製出來的東西,不但願影響原來的內容。objective-c
針對copy
就會引出一些面試題:數組
NSString
屬性,可使用strong
關鍵字修飾嗎?若是能夠,何時使用strong
修飾,何時使用copy
修飾?NSMutableArray
屬性,關鍵字使用copy
,像NSMutableArray
中添加元素會發生什麼現象?NSString
NSMutableString
NSArray
NSMutableArray
NSDictionary
NSMutableDictionary
調用copy
方法或者mutableCopy
方法,是深拷貝仍是淺拷貝?等等...app
下面咱們就來探究一下copy
atom
NSString
這個類表明不可變字符串。不可變意味着建立出來的字符串對象不能夠被修改str1
指向字符串對象str1
調用copy
方法,str2
指向copy
出來的對象str1
調用mutableCopy
方法,str3
指向mutableCopy
出來的對象- (void)viewDidLoad {
[super viewDidLoad];
NSString *str1 = @"test";
NSString *str2 = [str1 copy];
NSMutableString *str3 = [str1 mutableCopy];
NSLog(@"\n str1 -> %@ \n str2 -> %@ \n str3 -> %@", str1, str2, str3);
NSLog(@"\n str1 -> %p \n str2 -> %p \n str3 -> %p", str1, str2, str3);
}
// 打印結果:
2020-06-04 22:47:42.843279+0800 05_copy[34618:3042446]
str1 -> test
str2 -> test
str3 -> test
2020-06-04 22:47:42.843496+0800 05_copy[34618:3042446]
str1 -> 0x10c9a0020
str2 -> 0x10c9a0020
str3 -> 0x600001836fa0
複製代碼
從打印結果能夠看出:spa
copy
仍是mutaleCopy
方法都成功複製了」test「這個文本str1
和str2
都指向了同一個對象,str3
指向了另一個對象。畫圖分析一波:翻譯
str1
和st2
都指向了同一個對象,str3
指向了另一個對象。copy
方法會返回一個不可變對象,而調用mutableCopy
方法會返回一個可變對象。copy
執行完畢以後,徹底能夠指向以前的對象,反正沒辦法進行修改,這樣反而節省了內存空間。NSString
建立的對象,調用copy
方法不會建立新的對象,只是指針的拷貝,屬於淺拷貝。而調用mutableCopy
方法會建立一個與以前內容同樣的新的對象,屬於深拷貝。str1
指向該對象str1
調用copy
方法,使用str2
指向返回的對象str1
調用mutableCopy
方法,使用str3
指向返回的對象- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *str1 = [[NSMutableString alloc] initWithString:@"test"];
NSString *str2 = [str1 copy];
NSMutableString *str3 = [str1 mutableCopy];
NSLog(@"\n str1 -> %@ \n str2 -> %@ \n str3 -> %@", str1, str2, str3);
NSLog(@"\n str1 -> %p \n str2 -> %p \n str3 -> %p", str1, str2, str3);
}
// 打印結果:
2020-06-05 12:16:42.174359+0800 05_copy[35192:3110215]
str1 -> test
str2 -> test
str3 -> test
2020-06-05 12:16:42.174536+0800 05_copy[35192:3110215]
str1 -> 0x600003d282d0
str2 -> 0xbf8fdf3650605176
str3 -> 0x600003d28270
複製代碼
結果分析:3d
st1
和str2
和str3
的內容都是」test「,成功複製畫圖分析:指針
st1
是指向的是可變字符串,能夠進行修改str1調用
copy方法會從新建立一個新的不可變字符串,是深拷貝,當
str1進行修改的時候,
str2`中的值不會受到任何的影響str1
調用mutableCopy
方法會建立一個新的能夠變字符串,那麼就能夠對這個可變字符串進行修改,不影響其str1
和str2
,是深拷貝。而且三者互不影響。NSString
相似,就再也不說明了,直接看代碼- (void)viewDidLoad {
[super viewDidLoad];
NSArray *array1 = @[@"a", @"b", @"c"];
NSArray *array2 = [array1 copy];
NSMutableArray *array3 = [array1 mutableCopy];
NSLog(@"\n array1 -> %@ \n array2 -> %@ \n array3 -> %@", array1, array2, array3);
NSLog(@"\n array1 -> %p \n array2 -> %p \n array3 -> %p", array1, array2, array3);
}
// 打印結果:
2020-06-05 12:32:17.303200+0800 05_copy[35231:3117262]
array1 -> (
a,
b,
c
)
array2 -> (
a,
b,
c
)
array3 -> (
a,
b,
c
)
2020-06-05 12:32:17.303393+0800 05_copy[35231:3117262]
array1 -> 0x600003a0c360
array2 -> 0x600003a0c360
array3 -> 0x600003a0c0f0
複製代碼
打印結果分析:code
array1
array2
和 array3
存儲的地址值來看,調用copy
方法進行了淺拷貝,而調用mutableCopy
方法是深拷貝。也就是說,修改array4
裏面的值不會影響array1
和 array2
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *array1 = [[NSMutableArray alloc] initWithObjects:@"a", @"b", @"c", nil];
NSArray *array2 = [array1 copy];
NSMutableArray *array3 = [array1 mutableCopy];
NSLog(@"\n array1 -> %@ \n array2 -> %@ \n array3 -> %@", array1, array2, array3);
NSLog(@"\n array1 -> %p \n array2 -> %p \n array3 -> %p", array1, array2, array3);
}
// 打印結果:
2020-06-05 12:37:57.830060+0800 05_copy[35251:3120031]
array1 -> (
a,
b,
c
)
array2 -> (
a,
b,
c
)
array3 -> (
a,
b,
c
)
2020-06-05 12:37:57.830295+0800 05_copy[35251:3120031]
array1 -> 0x60000378d6e0
array2 -> 0x60000378d950
array3 -> 0x60000378d9b0
複製代碼
結果分析:
copy
仍是 mutable
方法都是深拷貝總結:
在OC中,其實還有不少相似的類好比NSDictionary
NSMutableDictionary
NSSet
NSMutableSet
結論都是同樣的。能夠本身去敲一段代碼來驗證。經常使用的我總結以下表:
copy | mutableCopy | |
---|---|---|
NSString | 返回NSString、淺拷貝 | 返回NSMutableString、深拷貝 |
NSMutableString | 返回NSString、深拷貝 | 返回NSMutableString、深拷貝 |
NSArray | 返回NSArray、淺拷貝 | 返回NSMutableArray、深拷貝 |
NSMutableArray | 返回NSArray、深拷貝 | 返回NSMutableArray、深拷貝 |
NSDictionary | 返回NSDictionary、淺拷貝 | 返回NSMutableDictionary、深拷貝 |
NSMutableDictionary | 返回NSDictionary、深拷貝 | 返回NSMutableDictionary、深拷貝 |
上面已經講清楚了,copy
和 mutableCopy
針對於不一樣的類返回結果以及是否產生新的對象作了分析和總結。
還遺留了點問題
NSString
屬性的時候,NSString
一般定義爲copy
定義成strong
行不行?若是二者都行,開發中該使用哪個?NSMutableArray
NSMutableString
NSMutableDictionary
的屬性,能不能用copy
,會不會有什麼問題?copy
修改爲 strong
,那麼打印結果又是什麼呢?@interface ViewController ()
@property (nonatomic, copy) NSString *str;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *mStr = [[NSMutableString alloc] initWithString:@"test"];
self.str = mStr;
[mStr appendString:@"haha"];
NSLog(@"mStr -> %@", mStr);
NSLog(@"self.str -> %@", self.str);
}
複製代碼
copy
關鍵字修飾的打印結果:mstr -> testhaha
self.str -> test
複製代碼
strong
關鍵字修飾的打印結果:mstr -> testhaha
self.str -> testhaha
複製代碼
這個問題的本質在於這句代碼,這句代碼的本質呢是在調用str的setter方法,最關鍵的地方就是要知道setter方法的內部是怎麼寫的呢?
self.str = mStr;
複製代碼
- (void)setStr:(NSString *)str {
if (_str != str) {
[_str release];
_str = [str copy]; // 最關鍵的地方
}
}
複製代碼
copy
方法,賦值給成員變量。str
就是mstr
,將一個可變字符串進行copy
後會建立一個新的對象,_str
指向了一個新的對象。因此你再去修改曾經的mStr
的值不會影響_str
的值。若是定義字符串屬性的時候,使用strong
關鍵字呢?仍是從本質出發,setter方法裏的這句代碼變了。變成retain
了。因此_str
指向原來的位置,當你修改mStr的值時候,_str確定會跟着改變。
_str = [str retain]; // 最關鍵的地方
複製代碼
若是你還不懂,再畫個圖給你解釋:
@interface ViewController ()
@property (nonatomic, copy) NSMutableArray *mArray;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.mArray = [[NSMutableArray alloc] initWithObjects:@"a", @"b", @"c", nil];
[self.mArray addObject:@"d"];
NSLog(@"%@", self.mArray);
}
複製代碼
-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x600002704060
複製代碼
copy
修飾,會建立一個新對象,而新對象是不可變數組,不可變數組調用addObject
方法怎麼可能找獲得呢?self.mArray = [[NSMutableArray alloc] initWithObjects:@"a", @"b", @"c", nil];
複製代碼
因此最終結果必定是崩潰。