相信對(duì)于有一定iOS開(kāi)發(fā)經(jīng)驗(yàn)的同學(xué)來(lái)說(shuō),對(duì)于copy關(guān)鍵字一定不陌生,從字義上來(lái)看,應(yīng)該就是復(fù)制一個(gè)對(duì)象,然后我們對(duì)于NSString類型的屬性,一般也用copy關(guān)鍵字。但是大家對(duì)于copy關(guān)鍵字真正有什么具體了解呢,什么時(shí)候用copy,什么時(shí)候用mutableCopy,區(qū)別又在哪里,對(duì)于內(nèi)存存儲(chǔ)上又有什么知識(shí)點(diǎn),我相信還有一部分同學(xué)一知半解。秉著鉆研探索的精神,我們來(lái)詳細(xì)的學(xué)習(xí)一下。
首先我們先說(shuō)兩個(gè)兩個(gè)概念:
淺復(fù)制:不拷貝對(duì)象本身,僅僅是拷貝指向?qū)ο蟮闹羔?br> 深復(fù)制:是直接拷貝整個(gè)對(duì)象內(nèi)存到另一塊內(nèi)存中
[圖片上傳失敗...(image-9e68bd-1524395644405)]
一般來(lái)說(shuō)像這種使用‘=’號(hào)賦值的對(duì)象,基本上都是淺復(fù)制
UIView * view1 = [[UIView alloc]init];
UIView * view2 = [[UIView alloc]init];
view1 = view2;
[圖片上傳失敗...(image-769ab5-1524395644405)]
內(nèi)存地址一樣的,很簡(jiǎn)單,所以它也是我們說(shuō)的淺復(fù)制之一。
然后我們來(lái)來(lái)看copy這關(guān)鍵字;
copy的字面意思就是“復(fù)制”,它是產(chǎn)生一個(gè)副本的過(guò)程,再來(lái)看在iOS里,copy與mutableCopy都是NSObject里的方法,一個(gè)NSObject的對(duì)象要想使用這兩個(gè)函數(shù),那么類必須實(shí)現(xiàn)NSCopying協(xié)議或NSMutableCopying協(xié)議,并且是實(shí)現(xiàn)了一般來(lái)說(shuō)我們用的很多系統(tǒng)里的容器類已經(jīng)實(shí)現(xiàn)了這些方法。
[圖片上傳失敗...(image-20e23a-1524395644405)]
如果不遵守協(xié)議,直接使用[xxx copy],那么會(huì)直接導(dǎo)致程序崩潰,比如UIView這個(gè)類就不允許使用copy
'NSInvalidArgumentException', reason: '-[UIView copyWithZone:]: unrecognized selector sent to instance 0x7fd5605099f0'
*** First throw call stack:
some error....
然后我們?cè)賮?lái)看copy關(guān)鍵字的特點(diǎn):
修改源對(duì)象的屬性和行為,不會(huì)影響副本對(duì)象
修改副本對(duì)象的屬性和行為,不會(huì)影響源對(duì)象
一個(gè)對(duì)象可以通過(guò)copy和mutableCopy方法來(lái)創(chuàng)建一個(gè)副本對(duì)象
copy:創(chuàng)建的是不可變副本(NSString,NSArray,NSDictionary)
mutableCopy:創(chuàng)建的是可變副本(NSMutableString,NSMutableArray,NSMutableDictionary)
原則就是:修改新(舊)對(duì)象,不影響舊(新)對(duì)象!而且不一定產(chǎn)生新的對(duì)象?。▌澲攸c(diǎn))
看個(gè)例子:
NSString * str = @"testStr";
NSMutableString * mutableStr = [str mutableCopy];
NSLog(@"%@,%p",str,str);
NSLog(@"%@,%p",mutableStr,mutableStr);
打印
testStr,0x103b9f068
testStr,0x600000264d80
可以看到兩個(gè)對(duì)象的內(nèi)容完全一樣,但是地址空間變了,說(shuō)明開(kāi)辟了一塊新內(nèi)存供給副本,為什么這個(gè)會(huì)產(chǎn)生新的對(duì)象呢?
1.因?yàn)樵瓌t 修改新(舊)對(duì)象,不影響舊(新)對(duì)象,所以生成一個(gè)新的對(duì)象
2.因?yàn)橐郧暗膶?duì)象是個(gè)不可變對(duì)象,而通過(guò)mutableCopy拷貝出來(lái)的對(duì)象必須是一個(gè)可變的對(duì)象,所以必須生成一個(gè)新的對(duì)象
同理:
NSMutableString * mutableStr = [NSMutableString stringWithFormat:@"mutableStr"];
NSMutableString * str = [mutableStr mutableCopy];
[str appendString:@"123"];
NSLog(@"%@,%p",mutableStr,mutableStr);
NSLog(@"%@,%p",str,str);
打印
mutableStr,0x6080000778c0
mutableStr123,0x608000077bc0
文字內(nèi)容不同,對(duì)象地址不同,修改新(舊)對(duì)象,不影響舊(新)對(duì)象
相同的
NSMutableString * mutableStr = [NSMutableString stringWithFormat:@"mutableStr"];
NSString * str = [mutableStr copy];
NSLog(@"%@,%p",mutableStr,mutableStr);
NSLog(@"%@,%p",str,str);
打印
mutableStr,0x600000075900
mutableStr,0x600000035360
原理一樣,使用copy關(guān)鍵字,產(chǎn)生了一個(gè)新的不可變的對(duì)象
以上的例子我們可以發(fā)先,使用copy或者mutableCopy都有產(chǎn)生新對(duì)象,現(xiàn)在我們?cè)賮?lái)看一個(gè)例子
NSString * str = @"str";
NSString * copyStr = [str copy];
NSLog(@"%@,%p",str,str);
NSLog(@"%@,%p",copyStr,copyStr);
打印
str,0x10c65e068
str,0x10c65e068
這下我們發(fā)現(xiàn),兩個(gè)對(duì)象的內(nèi)存地址完全一樣,所以系統(tǒng)并沒(méi)有創(chuàng)建一個(gè)新對(duì)象,這是為什么呢?
當(dāng)我們對(duì)一個(gè)不可變對(duì)象(NSString類型)使用copy關(guān)鍵字的時(shí)候,系統(tǒng)是不會(huì)產(chǎn)生一個(gè)新對(duì)象,因?yàn)樵瓉?lái)的對(duì)象是不能修改的,拷貝出來(lái)的對(duì)象也是不能修改的,那么既然兩個(gè)都不可以修改,所以這兩個(gè)對(duì)象永遠(yuǎn)也不會(huì)影響到另一個(gè)對(duì)象(符合我們說(shuō)的“修改新(舊)對(duì)象,不影響舊(新)對(duì)象”原則),系統(tǒng)為了節(jié)省內(nèi)存,所以就不會(huì)產(chǎn)生一個(gè)新的對(duì)象了。
那么問(wèn)題來(lái)了, copy到底是深拷貝還是淺拷貝?
我相信有的同學(xué)認(rèn)為只要是使用copy關(guān)鍵字,那么肯定都是深拷貝,這樣是很不嚴(yán)謹(jǐn)?shù)?,就比如上個(gè)例子,雖然使用了copy,但是指針地址是一樣,那么它就應(yīng)該是淺拷貝。
所以是否是深淺拷貝,是否創(chuàng)建新的對(duì)象,是由程序運(yùn)行的環(huán)境所造成的,并不是一概而論。
對(duì)于NSArray,NSDictionary,道理也是相同的。
現(xiàn)在再讓我們看下copy的內(nèi)存管理:
淺拷貝不會(huì)生成新的對(duì)象,所以系統(tǒng)會(huì)對(duì)以前的對(duì)象進(jìn)行一次retain,深拷貝會(huì)產(chǎn)生新的對(duì)象,系統(tǒng)不會(huì)對(duì)以前的對(duì)象進(jìn)行retain。
接著我們來(lái)看下copy與Block的配合使用
首先我們還是回顧一個(gè)概念
block默認(rèn)存儲(chǔ)在棧中,棧中的Block訪問(wèn)到的外界對(duì)象,不會(huì)對(duì)應(yīng)進(jìn)行retain
block如果在堆中,在block中訪問(wèn)了外界的對(duì)象,會(huì)對(duì)外界的對(duì)象進(jìn)行一次retian
因?yàn)閎lock在什么時(shí)候執(zhí)行是不確定的,所以如果block里外部對(duì)象被提前釋放了,那么如果這時(shí)候block執(zhí)行了,造成野指針異常,程序crash。
所以對(duì)于Block來(lái)說(shuō),我們一般都用copy關(guān)鍵字修飾.
#import <Foundation/Foundation.h>
typedef void(^TestBlock)(NSString * str);
@interface Model : NSObject
@property (nonatomic,copy) TestBlock testblock;
@end
使用copy保存block,這樣可以保住block中,避免以后調(diào)用block的時(shí)候,外界的對(duì)象已經(jīng)釋放了
作者:司機(jī)王
鏈接:http://www.itdecent.cn/p/700f58eb0b86
來(lái)源:簡(jiǎn)書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。