一、為什么要管理內(nèi)存
- 由于移動(dòng)設(shè)備的內(nèi)存極其有限,所以每個(gè)APP所占的內(nèi)存也是有限制的,當(dāng)app所占用的內(nèi)存較多時(shí)帆谍,系統(tǒng)就會(huì)發(fā)出內(nèi)存警告,這時(shí)需要回收一些不需要再繼續(xù)使用的內(nèi)存空間轴咱,比如回收一些不再使用的對(duì)象和變量等汛蝙。
- iOS應(yīng)用程序出現(xiàn)Crash(閃退), 90%的原因是內(nèi)存管理問題烈涮。
- 當(dāng)一個(gè)有十幾個(gè)或者幾十個(gè)類的工程中, 查找內(nèi)存問題是非常難的一件事, 所以一定要學(xué)好內(nèi)存管理, 這個(gè)很重要患雇。
二跃脊、內(nèi)存的管理范圍
- 任何繼承NSObject的對(duì)象,對(duì)其他的基本數(shù)據(jù)類型無效苛吱。
三酪术、內(nèi)存管理的錯(cuò)誤方式
- 內(nèi)存溢出和野指針異常
(1) 內(nèi)存溢出
內(nèi)存過多導(dǎo)致溢出, 導(dǎo)致Crash
(2) 野指針 (過度釋放)
指針指向未知的區(qū)域, 就是指針指向一塊被釋放的區(qū)域
四、內(nèi)存管理方式
(1) 垃圾回收機(jī)制: 程序員只需要開辟內(nèi)存, 而不需要以代碼的形式管理內(nèi)存, 系統(tǒng)會(huì)自動(dòng)判斷這部分內(nèi)存是否需要釋放翠储。(iOS不支持垃圾回收)
(2) MRC(Manual Reference Counting): 手動(dòng)管理引用計(jì)數(shù)
(3) ARC(Auto Reference Counting): 自動(dòng)管理引用計(jì)數(shù)(現(xiàn)在常用)
注意: ARC基于MRC進(jìn)行管理, 系統(tǒng)幫程序員添加了內(nèi)存管理的內(nèi)容
五绘雁、內(nèi)存管理--引用計(jì)數(shù)
在OC中每個(gè)對(duì)象內(nèi)部都有一個(gè)與之對(duì)應(yīng)的整數(shù)(retainCount),叫“引用計(jì)數(shù)器”
有retain, alloc, copy 會(huì)對(duì)引用計(jì)數(shù)加1
有release, autorelease 會(huì)對(duì)引用計(jì)數(shù)減1
當(dāng)對(duì)象的計(jì)數(shù)器為0的時(shí)候, 系統(tǒng)會(huì)調(diào)用對(duì)應(yīng)的dealloc方法
注意: 內(nèi)存管理, 你對(duì)對(duì)象操作完成后, 再進(jìn)行釋放
Man *man = [[Man alloc] init];
NSLog(@"%ld", man.retainCount);
[man retain];
[man release];
NSLog(@"%ld--%@", man.retainCount, man);
集合類型, 會(huì)對(duì)對(duì)象進(jìn)行引用計(jì)數(shù)
addObject 對(duì) 對(duì)象進(jìn)行引用計(jì)數(shù)加1
removeObject 對(duì) 對(duì)象進(jìn)行引用計(jì)數(shù)減1
NSMutableArray *arr = [NSMutableArray arrayWithObjects:man, nil];
NSLog(@"向數(shù)組中添加后的引用計(jì)數(shù) %ld", man.retainCount);
[arr removeAllObjects];
NSLog(@"從數(shù)組中移除后的引用計(jì)數(shù) %ld", man.retainCount);
六援所、ARC(自動(dòng)管理引用計(jì)數(shù))
-
怎樣關(guān)閉ARC進(jìn)入MRC模式?
如上圖所示, 將YES改為NO即為關(guān)閉ARC進(jìn)入MRC模式
-
如果需要對(duì)特定文件開啟或關(guān)閉ARC庐舟,可以在工程選項(xiàng)中選擇BuildPhases -> Compile Sources,在里面找到對(duì)應(yīng)文件住拭,雙擊后添加flag:
打開ARC:-fobjc-arc
關(guān)閉ARC:-fno-objc-arc
如圖所示, 為關(guān)閉ViewController的ARC, 如果需要打開ARC, 則輸入-fobjc-arc
ARC主要提供了4種修飾符挪略,他們分別是:__strong, __weak, __autoreleasing, __unsafe_unretained。
(1)__strong
表示引用為強(qiáng)引用在ARC下使用, 引用計(jì)數(shù)加1滔岳。對(duì)應(yīng)在定義property時(shí)的"strong"杠娱。所有對(duì)象只有當(dāng)沒有任何一個(gè)強(qiáng)引用指向時(shí),才會(huì)被釋放谱煤。
注意:如果在聲明引用時(shí)不加修飾符摊求,那么引用將默認(rèn)是強(qiáng)引用。當(dāng)需要釋放強(qiáng)引用指向的對(duì)象時(shí)刘离,需要將強(qiáng)引用置nil室叉。
(2)__weak
表示引用為弱引用。對(duì)應(yīng)在定義property時(shí)用的"weak"硫惕。弱引用不會(huì)影響對(duì)象的釋放茧痕,即只要對(duì)象沒有任何強(qiáng)引用指向,即使有100個(gè)弱引用對(duì)象指向也沒用恼除,該對(duì)象依然會(huì)被釋放踪旷。不過好在,對(duì)象在被釋放的同時(shí)缚柳,指向它的弱引用會(huì)自動(dòng)被置nil,這個(gè)技術(shù)叫zeroing weak pointer搪锣。這樣有效得防止無效指針秋忙、野指針的產(chǎn)生。
定義一個(gè)__weak類型的正確方式和錯(cuò)誤方式
NSString * __weak str = @"hehe"; // 正確构舟!
__weak NSString *str = @"hehe"; // 錯(cuò)誤灰追!
(3)__autoreleasing
表示在autorelease pool中自動(dòng)釋放對(duì)象的引用,和MRC時(shí)代autorelease的用法相同。定義property時(shí)不能使用這個(gè)修飾符弹澎,任何一個(gè)對(duì)象的property都不應(yīng)該是autorelease型的朴下。
NSString *str = [[[NSString alloc] initWithFormat:@"hehe"] autorelease]; // MRC下
NSString *__autoreleasing str = [[NSString alloc] initWithFormat:@"hehe"]; // ARC下
(4)__unsafe_unretained
ARC是在iOS 5引入的,而這個(gè)修飾符主要是為了在ARC剛發(fā)布時(shí)兼容iOS 4以及版本更低的設(shè)備苦蒿,因?yàn)檫@些版本的設(shè)備沒有weak pointer system殴胧,簡單的理解這個(gè)系統(tǒng)就是我們上面講weak時(shí)提到的,能夠在weak引用指向?qū)ο蟊会尫藕笈宄伲岩弥底詣?dòng)設(shè)為nil的系統(tǒng)团滥。這個(gè)修飾符在定義property時(shí)對(duì)應(yīng)的是"unsafe_unretained",實(shí)際可以將它理解為MRC時(shí)代的assign:純粹只是將引用指向?qū)ο蟊ㄇ浚瑳]有任何額外的操作灸姊,在指向?qū)ο蟊会尫艜r(shí)依然原原本本地指向原來被釋放的對(duì)象(所在的內(nèi)存區(qū)域)。所以非常不安全秉溉。
七力惯、MRC(手動(dòng)引用計(jì)數(shù))
在這個(gè)模式下使用alloc、new召嘶、copy創(chuàng)建一個(gè)對(duì)象父晶,該對(duì)象的retainCount都等于1,需要用release來釋放該對(duì)象苍蔬。就是誰創(chuàng)建的诱建,誰就去釋放。
如果你定義了一個(gè)屬性(刨除assign修飾外), 那么你就必須重寫dealloc的方法, 例:
如圖所示, 我定義了一個(gè)NSString的屬性, 所以對(duì)應(yīng)的重寫了dealloc這個(gè)方法.
本次對(duì)于內(nèi)存管理的梳理知識(shí)到這里結(jié)束, 這些只是基礎(chǔ)知識(shí), 日后我還會(huì)不斷的完善, 希望大俠們能夠指出不足, 互相學(xué)習(xí), 互相進(jìn)步, 謝謝各位.