相信對于有一定iOS開發(fā)經驗的同學來說痘绎,對于copy關鍵字一定不陌生泉哈,從字義上來看蛉幸,應該就是復制一個對象,然后我們對于NSString類型的屬性丛晦,一般也用copy關鍵字奕纫。但是大家對于copy關鍵字真正有什么具體了解呢,什么時候用copy烫沙,什么時候用mutableCopy匹层,區(qū)別又在哪里,對于內存存儲上又有什么知識點锌蓄,我相信還有一部分同學一知半解又固。秉著鉆研探索的精神仲器,我們來詳細的學習一下煤率。
首先我們先說兩個兩個概念:
淺復制:不拷貝對象本身仰冠,僅僅是拷貝指向對象的指針
深復制:是直接拷貝整個對象內存到另一塊內存中
[圖片上傳失敗...(image-9e68bd-1524395644405)]
一般來說像這種使用‘=’號賦值的對象,基本上都是淺復制
UIView * view1 = [[UIView alloc]init];
UIView * view2 = [[UIView alloc]init];
view1 = view2;
[圖片上傳失敗...(image-769ab5-1524395644405)]
內存地址一樣的蝶糯,很簡單洋只,所以它也是我們說的淺復制之一。
然后我們來來看copy這關鍵字昼捍;
copy的字面意思就是“復制”识虚,它是產生一個副本的過程,再來看在iOS里妒茬,copy與mutableCopy都是NSObject里的方法担锤,一個NSObject的對象要想使用這兩個函數,那么類必須實現(xiàn)NSCopying協(xié)議或NSMutableCopying協(xié)議乍钻,并且是實現(xiàn)了一般來說我們用的很多系統(tǒng)里的容器類已經實現(xiàn)了這些方法肛循。
[圖片上傳失敗...(image-20e23a-1524395644405)]
如果不遵守協(xié)議,直接使用[xxx copy]银择,那么會直接導致程序崩潰多糠,比如UIView這個類就不允許使用copy
'NSInvalidArgumentException', reason: '-[UIView copyWithZone:]: unrecognized selector sent to instance 0x7fd5605099f0'
*** First throw call stack:
some error....
然后我們再來看copy關鍵字的特點:
修改源對象的屬性和行為,不會影響副本對象
修改副本對象的屬性和行為浩考,不會影響源對象
一個對象可以通過copy和mutableCopy方法來創(chuàng)建一個副本對象
copy:創(chuàng)建的是不可變副本(NSString夹孔,NSArray,NSDictionary)
mutableCopy:創(chuàng)建的是可變副本(NSMutableString析孽,NSMutableArray搭伤,NSMutableDictionary)
原則就是:修改新(舊)對象,不影響舊(新)對象袜瞬!而且不一定產生新的對象A(劃重點)
看個例子:
NSString * str = @"testStr";
NSMutableString * mutableStr = [str mutableCopy];
NSLog(@"%@,%p",str,str);
NSLog(@"%@,%p",mutableStr,mutableStr);
打印
testStr,0x103b9f068
testStr,0x600000264d80
可以看到兩個對象的內容完全一樣,但是地址空間變了吞滞,說明開辟了一塊新內存供給副本佑菩,為什么這個會產生新的對象呢?
1.因為原則 修改新(舊)對象裁赠,不影響舊(新)對象殿漠,所以生成一個新的對象
2.因為以前的對象是個不可變對象,而通過mutableCopy拷貝出來的對象必須是一個可變的對象佩捞,所以必須生成一個新的對象
同理:
NSMutableString * mutableStr = [NSMutableString stringWithFormat:@"mutableStr"];
NSMutableString * str = [mutableStr mutableCopy];
[str appendString:@"123"];
NSLog(@"%@,%p",mutableStr,mutableStr);
NSLog(@"%@,%p",str,str);
打印
mutableStr,0x6080000778c0
mutableStr123,0x608000077bc0
文字內容不同绞幌,對象地址不同,修改新(舊)對象一忱,不影響舊(新)對象
相同的
NSMutableString * mutableStr = [NSMutableString stringWithFormat:@"mutableStr"];
NSString * str = [mutableStr copy];
NSLog(@"%@,%p",mutableStr,mutableStr);
NSLog(@"%@,%p",str,str);
打印
mutableStr,0x600000075900
mutableStr,0x600000035360
原理一樣莲蜘,使用copy關鍵字谭确,產生了一個新的不可變的對象
以上的例子我們可以發(fā)先,使用copy或者mutableCopy都有產生新對象票渠,現(xiàn)在我們再來看一個例子
NSString * str = @"str";
NSString * copyStr = [str copy];
NSLog(@"%@,%p",str,str);
NSLog(@"%@,%p",copyStr,copyStr);
打印
str,0x10c65e068
str,0x10c65e068
這下我們發(fā)現(xiàn)逐哈,兩個對象的內存地址完全一樣,所以系統(tǒng)并沒有創(chuàng)建一個新對象问顷,這是為什么呢昂秃?
當我們對一個不可變對象(NSString類型)使用copy關鍵字的時候,系統(tǒng)是不會產生一個新對象杜窄,因為原來的對象是不能修改的肠骆,拷貝出來的對象也是不能修改的,那么既然兩個都不可以修改塞耕,所以這兩個對象永遠也不會影響到另一個對象(符合我們說的“修改新(舊)對象蚀腿,不影響舊(新)對象”原則),系統(tǒng)為了節(jié)省內存扫外,所以就不會產生一個新的對象了莉钙。
那么問題來了, copy到底是深拷貝還是淺拷貝畏浆?
我相信有的同學認為只要是使用copy關鍵字胆胰,那么肯定都是深拷貝,這樣是很不嚴謹的刻获,就比如上個例子蜀涨,雖然使用了copy,但是指針地址是一樣蝎毡,那么它就應該是淺拷貝厚柳。
所以是否是深淺拷貝,是否創(chuàng)建新的對象沐兵,是由程序運行的環(huán)境所造成的别垮,并不是一概而論。
對于NSArray扎谎,NSDictionary碳想,道理也是相同的。
現(xiàn)在再讓我們看下copy的內存管理:
淺拷貝不會生成新的對象毁靶,所以系統(tǒng)會對以前的對象進行一次retain胧奔,深拷貝會產生新的對象,系統(tǒng)不會對以前的對象進行retain预吆。
接著我們來看下copy與Block的配合使用
首先我們還是回顧一個概念
block默認存儲在棧中龙填,棧中的Block訪問到的外界對象,不會對應進行retain
block如果在堆中,在block中訪問了外界的對象岩遗,會對外界的對象進行一次retian
因為block在什么時候執(zhí)行是不確定的扇商,所以如果block里外部對象被提前釋放了,那么如果這時候block執(zhí)行了宿礁,造成野指針異常案铺,程序crash。
所以對于Block來說窘拯,我們一般都用copy關鍵字修飾.
#import <Foundation/Foundation.h>
typedef void(^TestBlock)(NSString * str);
@interface Model : NSObject
@property (nonatomic,copy) TestBlock testblock;
@end
使用copy保存block红且,這樣可以保住block中,避免以后調用block的時候涤姊,外界的對象已經釋放了
作者:司機王
鏈接:http://www.reibang.com/p/700f58eb0b86
來源:簡書
著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權嗤放,非商業(yè)轉載請注明出處思喊。