參考文獻《招聘一個靠譜的iOS》
深拷貝和淺拷貝
由上面的圖我們可以明確地看出,
淺拷貝(Shallow copy)可以說是指針復制,它們指向共同的內存地址,沒有產生新的對象,源對象和副本對象是同一對象,相當于做一次retain操作,引用計數加1
深拷貝(Deep copy)是指內容拷貝,分別指向了不同的內存地址,它產生的新的對象,源對象的引用計數不變,副本對象的引用計數為1.
什么集合類對象和非集合類對象
非集合類對象(NSString,NSMutableString,NSData,NSNumber)
集合類對象(NSArray,NSMutableArray,NSDictionary,NSSet...)
系統對象的copy與mutableCopy方法
- (id)copy;
- (id)mutableCopy;
不管是集合類對象,還是非集合類對象,接收到copy和mutableCopy消息時,都遵循以下準則:
1.copy返回imutable對象;所以,如果對copy返回值使用mutable對象接口就會crash;
2.mutableCopy返回mutable對象;
那什么是imutable對象和mutable對象?
imutable對象就是不可變的對象。像NSString不可以拼接字符串、NSArray也不可添加元素...這種就是不可變對象。
mutable對象就是可變的對象。像NSMutableArray可以動態添加元素,NSMutableString可以拼接字符串...這種就是可變的對象。
舉個例子:
NSMutableString 通過 copy 返回的是 NSString 對象,但是因為返回值是id類型,所以我們可以使用NSMutableString來接收,但是,當你使用NSMutableString獨有的對象接口的時候,就會crash,提示找不到對應的API.
NSString 通過 mutableCopy 返回的是 NSMutableString對象
例子
非集合類對象(NSString,NSMutableString,NSData,NSNumber...)的copy 和 mutableCopy
NSString *str1 = @"imutable";? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
NSString *Str2 = [str1 copy];? ? ? ? ? ? ? ? ? ? ? ? ?
NSMutableString *Str3 = [str1 mutableCopy];?
NSMutableString *str4 = [[NSMutableString alloc]initWithString:@"mutable"];
NSMutableString *str5 = [str4 copy];
NSMutableString *str6 = [str4 mutableCopy]; ?
[str6 appendFormat:@"hello"];
[str5 appendFormat:@"hello"]; ? // crash
通過lldb查看變量內存地址,結果如下
總結,非集合類中
1.對 不可變對象 進行 copy ,屬于淺拷貝。
2.對 不可變對象 進行 mutableCopy ,屬于深拷貝
3. 對 可變對象? 進行 copy, 屬于深拷貝
4. 對可變對象進行mutableCopy, 屬于深拷貝
注意:運行到這句 [str5 appendFormat:@"hello"]; 的時候會crash,原因就是 copy 返回的對象是 NSString 對象,然后你用NSMutableString去接受,并調用NSMutableString獨有的 appendFormat 方法。
集合類對象(NSArray,NSDictionary,NSSet...)的copy 和 mutableCopy
NSArray *array0 = @[@"a",@"b",@"c"];
NSArray *array1 = [array0 copy];
NSArray *array2 = [array0 mutableCopy];
NSMutableArray *array3 = [[NSMutableArray alloc]initWithObjects:@"a",@"b",@"c", nil];
NSMutableArray *array4 = [array3 copy];
NSMutableArray *array5 = [array3 mutableCopy];
通過lldb查看變量內存地址,結果如下
總結,在集合類中
1.對不可變對象進行copy,屬于淺拷貝。
2.對不可變對象進行mutableCopy,屬于單層深拷貝
3. 對可變對象進行copy,屬于單層深拷貝
4. 對可變對象進行mutableCopy,屬于單層深拷貝
什么是單層深拷貝?
單層深拷貝是指集合對象的內容復制僅限于對象本身,對象元素仍然是指針復制,例如對 array0 進行? mutableCopy,我們對 array0 進行了內容拷貝,但是array0 里面的元素,我們任然只有指針復制。
@property中copy關鍵字
當我們使用一個copy關鍵字聲明一個對象的時候, 調用 set 方法的時候,copy關鍵字會為對象自動copy一個副本,舉個例子:
@property (nonatomic, copy) NSArray *array;
- (void)setArray:(NSArray *)array {
_array = [array copy]; ?//這里為array ?copy 了一個副本
}
如果我們直接用strong關鍵字的話,又是怎樣的呢?
@property (nonatomic, strong) NSArray *array;
- (void)setArray:(NSArray *)array {
//他們指向了同一塊內存空間,如果此時傳入的array是一個NSMutableArray的話,
//self.array可能會在不知情的情況下被修改。這種情況下面還會再說到
_array = array; ?
}
為什么用@property聲明的NSString(或NSArray,NSDictionary)經常使用copy關鍵字?使用strong關鍵字,會有什么問題?
我們先舉一個例子看看使用strong會有造成什么后果
定義一個以 strong 修飾的 array:
@property (nonatomic , strong) NSArray*array;
.m實現代碼為了方便,我用截圖
總結:
1.因為父類指針可以指向子類對象(如上面的NSArray對象可以指向一個NSMutableArray對象),使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個不可變的副本.
2.如果我們使用是 strong ,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性.
上面解釋了為什么用@property聲明不可變對象(NSString、NSArray,NSDictionary)時,經常用copy關鍵字,接下來我們來解釋為什么要用strong關鍵字來聲明可變對象(NSMutableString、NSMutableArray、NSMutableDictionary),而不用copy對象?
假如我們用copy關鍵字 來聲明一個NSMutableArray對象。
@property (nonatomic, copy) NSMutableArray *mutableArray;
.m實現方法
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@1,@2,nil];
self.mutableArray = array1;
[self.mutableArray removeObjectAtIndex:0]; //crash
上面執行到 removeObjectAtIndex 會crash,原因是 mutableArray 是用copy關鍵字聲明的,copy返回的是一個不可變對象,也就是NSMutableArray會變成NSArray,然后你再執行removeObjectAtIndex方法,就會報找不到這個方法而crash