我覺得我這個(gè)人什么都好字管,就是有時(shí)候有點(diǎn)懶 啰挪,搞了三年多iOS開發(fā)了,回頭一看網(wǎng)上都沒有留下自己的痕跡嘲叔,昨天跟一位CTO聊天亡呵,講到職業(yè)發(fā)展的道路,他跟我說借跪,作為一個(gè)技術(shù)人員政己,要懂得分享,要學(xué)會(huì)傳播自己的知識(shí),而不能掖著藏著歇由,你在分享的過程中卵牍,不僅自己對(duì)知識(shí)的理解更到位,還能擴(kuò)大自己在圈子里的影響力沦泌,走上更好的職業(yè)發(fā)展之路糊昙,聽完他的話,我覺得我浪費(fèi)了幾年美好的光陰谢谦,所以我決定慢慢的把自己懂得東西分享出來释牺,跟大家一起交流一起進(jìn)步,如果有講的不好的地方回挽,希望大家多多斧正没咙。
下面進(jìn)入今天的主題,一直以來不斷的有人問我關(guān)于內(nèi)存管理方面的內(nèi)容千劈,我也回答了很多祭刚,今天我就將內(nèi)存管理方面的一些重要的東西拆成小的模塊逐一的講解下,今天要講的是字符串里面為什么要用copy來修飾墙牌。 請(qǐng)看下面的代碼截圖:我定義了四個(gè)屬性涡驮,分別用strong來修飾的NSString和用copy來修飾的NSString,這樣會(huì)形成一個(gè)對(duì)比,方便大家理解
@property(nonatomic, strong)NSString *testString;
@property(nonatomic, strong)NSMutableString *test2String;
@property(nonatomic, copy)NSString *test3String;
@property(nonatomic, strong)NSMutableString *test4String;
定義好屬性后我們對(duì)其進(jìn)行賦值操作 :
self.testString = @"aaaa";
self.test2String = [[NSMutableString alloc] init];
self.test2String.string = @"nuli";
self.testString = _test2String;
self.test3String = @"bbb";
NSMutableString *test4String = [[NSMutableString alloc] init];
self.test4String = test4String;
self.test4String.string = [@"fendou" mutableCopy];
self.test3String = _test4String;
我們首先給testString進(jìn)行了賦值喜滨,然后生成了一個(gè)test2String捉捅,并對(duì)其賦值,然后分別再來看testString和test2String的內(nèi)存地址和值虽风,我們通過打斷點(diǎn)使用lldb來查看,當(dāng)程序執(zhí)行到斷點(diǎn)處的時(shí)候
![2.png](http://upload-images.jianshu.io/upload_images/4316454-ec471757b6404b7b.png? imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
當(dāng)我們將test2String賦值給testString之后內(nèi)存地址和值如下圖所示:
可以看到兩個(gè)字符串的值和內(nèi)存一樣棒口,這個(gè)時(shí)候我們來改變test2String字符串的值然后在來查看:
我們會(huì)發(fā)現(xiàn)兩個(gè)字符串的值同時(shí)發(fā)生了改變,但內(nèi)存地址是一致的焰情,所以我們可以肯定兩個(gè)字符串是共用的一份內(nèi)存陌凳,也就是說使用strong來修飾NSString,在你對(duì)其進(jìn)行賦值的時(shí)候内舟,只是復(fù)制了一個(gè)指針而已合敦,并沒有分配新的內(nèi)存空間,這樣的話會(huì)導(dǎo)致一個(gè)什么樣的問題呢验游?就是你在程序的其他地方更改了test2String的值后testString的值也會(huì)發(fā)生改變充岛,所以這個(gè)會(huì)存在潛在的數(shù)據(jù)污染的風(fēng)險(xiǎn),如非明確共用一份內(nèi)存外耕蝉,不建議使用strong來修飾NSString
下面我們來看看使用copy修飾的字符串賦值的時(shí)候是怎么樣的崔梗,請(qǐng)看下圖:當(dāng)程序執(zhí)行到這個(gè)地方的時(shí)候:
將test4String的值賦給test3String之后內(nèi)存地址和值如下圖所示:
可以看到test3String字符串的內(nèi)存和內(nèi)容都沒有發(fā)生改變,改變test3String 的值test4String的地址和內(nèi)容也沒有發(fā)生任何改變垒在,所有我們可以知道使用copy關(guān)鍵字修飾的字符串在賦值的時(shí)候會(huì)重新生成一塊新的內(nèi)存蒜魄,然后把另一個(gè)字符串的內(nèi)容復(fù)制進(jìn)來,這就相當(dāng)于兩個(gè)字符串都存在于兩個(gè)獨(dú)立的內(nèi)存空間,所以改變?nèi)魏我粋€(gè)字符串的值對(duì)另外一個(gè)字符串都不會(huì)有任何的影響谈为,這個(gè)也就是我們所說的深復(fù)制旅挤。
修飾數(shù)組時(shí)其實(shí)也一樣,如下面伞鲫,我定義了兩個(gè)可變數(shù)組:
@property(nonatomic,copy,nullable)NSMutableArray<YCCombineModel *> *historyOrderArray;
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *arr = [NSMutableArray array];
_historyOrderArray = a;
for (int i = 0; i<20; i++) {
YCCombineModel *model = [[YCCombineModel alloc] init];
model.isSelected = NO;
[_historyOrderArray addObject:model];
}
}
當(dāng)我用copy修飾數(shù)組的時(shí)候粘茄,代碼執(zhí)行到
[_historyOrderArray addObject:model];
這行代碼的時(shí)候會(huì)掛掉,要理解崩潰的原因先要知道一些常識(shí)性的東西:
在iOS開發(fā)中秕脓,對(duì)象之間傳值都是使用引用計(jì)數(shù)器增加的方式柒瓣,這種方式的適用于當(dāng)對(duì)象的某屬性的值改變時(shí),引用該對(duì)象的不同指針會(huì)同時(shí)改變吠架,因?yàn)檫@兩個(gè)指針指向的是同一個(gè)內(nèi)存地址芙贫,當(dāng)一個(gè)指針執(zhí)行的對(duì)象屬性值發(fā)生改變時(shí),不影響另一個(gè)對(duì)象诵肛,那么需要分配兩個(gè)不同的內(nèi)存地址屹培,也就是說,我們就不可以采用retain關(guān)鍵字了怔檩,而是要采用copy 關(guān)鍵字。如:
property (nonatomic,copy)NSSting * name;
一:
區(qū)分深復(fù)制與淺復(fù)制蓄诽,一般只有可變的mutableCopy到mutableCopy的Copy才是淺復(fù)制薛训,不產(chǎn)生副本只是retain count增加。
副本的特點(diǎn):彼此的內(nèi)容一樣,擁有相同的方法,在內(nèi)存中有兩個(gè)對(duì)象.
1.copy
1> 如果對(duì)象有 可變/不可變 版本的區(qū)別, copy方法只能拷貝出不可變的副本.
2> 如果對(duì)象沒有 可變/不可變 版本的區(qū)別,copy方法只是建立一個(gè)副本.
2.mutableCopy
建立對(duì)象的可變版本的副本(如果對(duì)象有 可變/不可變 版本之分)
3.深拷貝/淺拷貝
1>深拷貝:如果建立出新的副本,在內(nèi)存中有兩個(gè)對(duì)象.就是深拷貝.
可變 -> 不可變 (copy)
可變 -> 可變 (mutableCopy)
不可變 -> 可變 (mutableCopy)
2.淺拷貝:不會(huì)建立新的副本, 只是引用計(jì)數(shù)加1.
不可變 -> 不可變 (因?yàn)閮蓚€(gè)不可變對(duì)象誰都不會(huì)被改變,也就沒必要建立副本)
因?yàn)槎x了copy修飾屬性后 在執(zhí)行其setter方法的時(shí)候會(huì)先release掉舊值然后retain 新值仑氛,并且上面那種賦值實(shí)際就相當(dāng)于 _historyOrderArray = [arr copy]; copy后就會(huì)返回一個(gè)NSArray類型的對(duì)象乙埃。所以調(diào)用addObject方法就會(huì)報(bào)unrecognized selector sent to instance 0x14e14970錯(cuò)誤, 但是如果使用strong來修飾_historyOrderArray就相當(dāng)于直接賦值,不會(huì)報(bào)錯(cuò)锯岖。
看完兩個(gè)的對(duì)比之后,我相信你對(duì)字符串和數(shù)組的修飾符的使用有了一個(gè)新的認(rèn)識(shí)。感謝您花費(fèi)寶貴的時(shí)間來閱讀此文益缎!如果有錯(cuò)誤的話歡迎大家多多指點(diǎn)掂名,當(dāng)然有想深入交流的也可以加我微信:bubiandeai05