我覺得我這個人什么都好,就是有時候有點懶 ,搞了三年多iOS開發(fā)了,回頭一看網上都沒有留下自己的痕跡,昨天跟一位CTO聊天,講到職業(yè)發(fā)展的道路,他跟我說,作為一個技術人員,要懂得分享,要學會傳播自己的知識,而不能掖著藏著,你在分享的過程中,不僅自己對知識的理解更到位,還能擴大自己在圈子里的影響力,走上更好的職業(yè)發(fā)展之路,聽完他的話,我覺得我浪費了幾年美好的光陰,所以我決定慢慢的把自己懂得東西分享出來,跟大家一起交流一起進步,如果有講的不好的地方,希望大家多多斧正。
下面進入今天的主題,一直以來不斷的有人問我關于內存管理方面的內容,我也回答了很多,今天我就將內存管理方面的一些重要的東西拆成小的模塊逐一的講解下,今天要講的是字符串里面為什么要用copy來修飾。 請看下面的代碼截圖:我定義了四個屬性,分別用strong來修飾的NSString和用copy來修飾的NSString,這樣會形成一個對比,方便大家理解
@property(nonatomic, strong)NSString *testString;
@property(nonatomic, strong)NSMutableString *test2String;
@property(nonatomic, copy)NSString *test3String;
@property(nonatomic, strong)NSMutableString *test4String;
定義好屬性后我們對其進行賦值操作 :
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進行了賦值,然后生成了一個test2String,并對其賦值,然后分別再來看testString和test2String的內存地址和值,我們通過打斷點使用lldb來查看,當程序執(zhí)行到斷點處的時候


當我們將test2String賦值給testString之后內存地址和值如下圖所示:
可以看到兩個字符串的值和內存一樣,這個時候我們來改變test2String字符串的值然后在來查看:

我們會發(fā)現(xiàn)兩個字符串的值同時發(fā)生了改變,但內存地址是一致的,所以我們可以肯定兩個字符串是共用的一份內存,也就是說使用strong來修飾NSString,在你對其進行賦值的時候,只是復制了一個指針而已,并沒有分配新的內存空間,這樣的話會導致一個什么樣的問題呢?就是你在程序的其他地方更改了test2String的值后testString的值也會發(fā)生改變,所以這個會存在潛在的數據污染的風險,如非明確共用一份內存外,不建議使用strong來修飾NSString
下面我們來看看使用copy修飾的字符串賦值的時候是怎么樣的,請看下圖:當程序執(zhí)行到這個地方的時候:


將test4String的值賦給test3String之后內存地址和值如下圖所示:

可以看到test3String字符串的內存和內容都沒有發(fā)生改變,改變test3String 的值test4String的地址和內容也沒有發(fā)生任何改變,所有我們可以知道使用copy關鍵字修飾的字符串在賦值的時候會重新生成一塊新的內存,然后把另一個字符串的內容復制進來,這就相當于兩個字符串都存在于兩個獨立的內存空間,所以改變任何一個字符串的值對另外一個字符串都不會有任何的影響,這個也就是我們所說的深復制。
修飾數組時其實也一樣,如下面,我定義了兩個可變數組:
@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];
}
}
當我用copy修飾數組的時候,代碼執(zhí)行到
[_historyOrderArray addObject:model];
這行代碼的時候會掛掉,要理解崩潰的原因先要知道一些常識性的東西:
在iOS開發(fā)中,對象之間傳值都是使用引用計數器增加的方式,這種方式的適用于當對象的某屬性的值改變時,引用該對象的不同指針會同時改變,因為這兩個指針指向的是同一個內存地址,當一個指針執(zhí)行的對象屬性值發(fā)生改變時,不影響另一個對象,那么需要分配兩個不同的內存地址,也就是說,我們就不可以采用retain關鍵字了,而是要采用copy 關鍵字。如:
property (nonatomic,copy)NSSting * name;
一:
區(qū)分深復制與淺復制,一般只有可變的mutableCopy到mutableCopy的Copy才是淺復制,不產生副本只是retain count增加。
副本的特點:彼此的內容一樣,擁有相同的方法,在內存中有兩個對象.
1.copy
1> 如果對象有 可變/不可變 版本的區(qū)別, copy方法只能拷貝出不可變的副本.
2> 如果對象沒有 可變/不可變 版本的區(qū)別,copy方法只是建立一個副本.
2.mutableCopy
建立對象的可變版本的副本(如果對象有 可變/不可變 版本之分)
3.深拷貝/淺拷貝
1>深拷貝:如果建立出新的副本,在內存中有兩個對象.就是深拷貝.
可變 -> 不可變 (copy)
可變 -> 可變 (mutableCopy)
不可變 -> 可變 (mutableCopy)
2.淺拷貝:不會建立新的副本, 只是引用計數加1.
不可變 -> 不可變 (因為兩個不可變對象誰都不會被改變,也就沒必要建立副本)
因為定義了copy修飾屬性后 在執(zhí)行其setter方法的時候會先release掉舊值然后retain 新值,并且上面那種賦值實際就相當于 _historyOrderArray = [arr copy]; copy后就會返回一個NSArray類型的對象。所以調用addObject方法就會報unrecognized selector sent to instance 0x14e14970錯誤, 但是如果使用strong來修飾_historyOrderArray就相當于直接賦值,不會報錯。
看完兩個的對比之后,我相信你對字符串和數組的修飾符的使用有了一個新的認識。感謝您花費寶貴的時間來閱讀此文!如果有錯誤的話歡迎大家多多指點,當然有想深入交流的也可以加我微信:bubiandeai05