? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?ios內(nèi)存管理
一.前言
在ios中,系統(tǒng)對(duì)每個(gè)程序運(yùn)行時(shí)內(nèi)存的占有都有一個(gè)限制,默認(rèn)都是幾十兆左右,當(dāng)程序占用內(nèi)存的大小超過(guò)限制時(shí),程序可能就會(huì)被強(qiáng)制退出.
在內(nèi)存中,分為堆和棧,棧中主要存放變量,堆中主要存放對(duì)象糙捺。棧中的東西是系統(tǒng)自動(dòng)收回的,當(dāng)一個(gè)變量使用完成后,存放在棧中的東西會(huì)立即被收回恬汁。但堆中存儲(chǔ)的東西是不會(huì)被隨便回收的在岂。
由于移動(dòng)設(shè)備內(nèi)存有限,所以我們會(huì)對(duì)內(nèi)存進(jìn)行嚴(yán)格的管理,以避免內(nèi)存泄露造成資源浪費(fèi)。
在OC中,只有對(duì)象才屬于內(nèi)存管理范圍,例如int ,float,struct等基本數(shù)據(jù)類(lèi)型不存在內(nèi)存管理的概念,在ios開(kāi)發(fā)中,對(duì)內(nèi)存的管理實(shí)際上是對(duì)引用計(jì)數(shù)器的管理。
相關(guān)概念:
僵尸對(duì)象:所占用的內(nèi)存被回收的對(duì)象,僵尸對(duì)象不能再使用。
野指針:指向僵尸對(duì)象的指針井濒,給野指針發(fā)消息會(huì)報(bào)錯(cuò)。
空指針:沒(méi)有指向任何東西的指針,給空指針發(fā)送消息不會(huì)報(bào)錯(cuò)列林。
目前OC內(nèi)存管理方式有三種:
(1)自動(dòng)垃圾收集(Automatic Garbage Collection)
(2)MRC(Manual Reference Counting,手動(dòng)引用計(jì)數(shù)器)和自動(dòng)釋放池(Autorelease)
(3)ARC(Automatic Reference Counting,自動(dòng)引用計(jì)數(shù)器)
自動(dòng)垃圾收集:
在OC2.0中,有一種自動(dòng)垃圾收集的內(nèi)存管理形式,通過(guò)垃圾自動(dòng)收集,系統(tǒng)能檢測(cè)出對(duì)象是否擁有其他對(duì)象,當(dāng)程序運(yùn)行期間,不被引用的對(duì)象就會(huì)被釋放瑞你。
但是在ios運(yùn)行環(huán)境中不支持自動(dòng)垃圾收集,在OS X環(huán)境才支持,不過(guò)蘋(píng)果推薦使用ARC進(jìn)行替代。
MRC和自動(dòng)釋放池子:
? 1. 引用計(jì)數(shù)器的概念:
即一個(gè)對(duì)象被引用的次數(shù),每個(gè)對(duì)象的引用計(jì)數(shù)器占4個(gè)字節(jié)希痴。
說(shuō)明當(dāng)一個(gè)對(duì)象被創(chuàng)建的時(shí)候,該對(duì)象的默認(rèn)RC為1,當(dāng)該對(duì)象被引用一次,需要調(diào)用retain方法,使RC的值加1,當(dāng)指針失去對(duì)該對(duì)象的引用,需要調(diào)用release方法,使RC的值減1者甲,當(dāng)RC的值等于0時(shí),該對(duì)象被系統(tǒng)自動(dòng)銷(xiāo)毀回收砌创。
2.對(duì)象的銷(xiāo)毀
? 當(dāng)一個(gè)對(duì)象的RC為0時(shí)虏缸,那么它將被銷(xiāo)毀,其占用的內(nèi)存會(huì)被系統(tǒng)回收.
? 當(dāng)一個(gè)對(duì)被銷(xiāo)毀時(shí),系統(tǒng)會(huì)自動(dòng)向?qū)ο蟀l(fā)送一條dealloc消息
? 一般會(huì)重寫(xiě)dealloc方法,在這里釋放相關(guān)資源,dealloc就是對(duì)象的遺言?
? 一旦重寫(xiě)了dealloc方法,就必須調(diào)用[super dealloc],并且放在最后面調(diào)用
? 對(duì)象一旦被回收了,它占用的內(nèi)存不再可用,堅(jiān)持使用就會(huì)導(dǎo)致指針錯(cuò)誤
3.MRC
通過(guò)人為的方式來(lái)控制引用計(jì)數(shù)器的增減,影響對(duì)象RC值的方式有以下幾種:
(1)new, alloc,copy,mutableCopy這幾種方式來(lái)創(chuàng)建一個(gè)新的對(duì)象,此時(shí)RC的默認(rèn)值為1.
(2)retain,對(duì)象調(diào)用retain方法,RC的值加1.
(3)release,對(duì)象調(diào)用release方法嫩实,RC的值減1.
(4)dealloc,dealloc方法并不會(huì)影響RC值,但是當(dāng)RC值為0時(shí),系統(tǒng)會(huì)調(diào)用dealloc方法來(lái)銷(xiāo)毀對(duì)象.
例如:
通過(guò)上面代碼我們知道刽辙,成員變量的設(shè)值和取值方法是手動(dòng)生成的,而且setter方法中成員變量的引用計(jì)數(shù)器也是手動(dòng)設(shè)置,我們可以通過(guò)@property和相應(yīng)關(guān)鍵字來(lái)由編譯器生成.
關(guān)于在MRC中@property關(guān)鍵字如下:
(1)assign和retain和copy
這幾個(gè)關(guān)鍵字用于setter方法的內(nèi)存管理,如果使用assign(一般用于非OC對(duì)象),那么將直接進(jìn)行賦值操作;如果使用retain(一般用于OC對(duì)象),那么將retain新值,release舊值;如果使用copy,那么將release舊值,copy新值。
(2)nonatomic和atomic
這兩個(gè)關(guān)鍵字用于多線程管理,nonatomic的性能高,atomic的性能低.不顯示則atomic為默認(rèn)值
(3)readwrite和readonly
這兩個(gè)關(guān)鍵字說(shuō)明是否生成setter方法,readwrite將自動(dòng)生成setter方法和getter方法甲献。readonly則只生成getter方法
如果使用@property屬性,上述代碼可以改為:
4.循環(huán)引用內(nèi)存管理原則
對(duì)于兩個(gè)類(lèi)B包含A,A包含B的循環(huán)引用情況下,看如下代碼:
當(dāng)執(zhí)行Book*b1=[[Book1 alloc]init]后;b1指向Book1宰缤。
當(dāng)執(zhí)行Book2*b2=[[Book2 alloc]init]后,b2指向Book2。
當(dāng)執(zhí)行b1.book2=b2后;Book1中的成員變量_book2指向Book2。
當(dāng)執(zhí)行b2.book1=b1后;Book2中的成員變量_book1指向Book1慨灭。
此時(shí)Book1的引用計(jì)數(shù)器RC=2;Book2的引用計(jì)數(shù)器RC=2朦乏。
當(dāng)執(zhí)行[b1 release]后,b1釋放對(duì)Book1的控制權(quán),此時(shí)Book1的引用計(jì)數(shù)器RC=2-1=1。
當(dāng)執(zhí)行[b2 release]后,b2釋放對(duì)Book2的控制權(quán),此時(shí)Book2的引用計(jì)數(shù)器RC=2-1=1氧骤。
那么由于仍有指針指向Book1和Book2,所以Book1和Book2不會(huì)被銷(xiāo)毀釋放,這樣就造成內(nèi)存泄露.
對(duì)于上面的情況,只需要在Book1或者Book2的任意一端使用assign,如此就能避免互相引用呻疹。
對(duì)于下面這種循環(huán)引用情況,只能使用assign。
如果@property(nonatomic,assign)id instance中的assign換成retain,也會(huì)造成內(nèi)存泄露,因?yàn)閕d為泛型筹陵。
5.Autorelease Pool的使用
顧名思義刽锤,Autorelease即自動(dòng)釋放對(duì)象,不需要我們手動(dòng)釋放。從上面的代碼中可以知道,在main函數(shù)中,創(chuàng)建對(duì)象obj后,總需要調(diào)用[obj release]方法,這樣無(wú)疑工作量變大,且對(duì)我們的工作無(wú)多大意義惶翻。為了減少這種無(wú)意義的工作姑蓝,我們可以使用Autorelease Pool。
在ios程序運(yùn)行的過(guò)程中,會(huì)創(chuàng)建無(wú)數(shù)個(gè)池子吕粗。這些池子都是以棧結(jié)構(gòu)存在(先進(jìn)后出),當(dāng)一個(gè)對(duì)象調(diào)用autorelease方法時(shí),會(huì)將這個(gè)對(duì)象放到棧頂?shù)尼尫懦亍?/p>
Autorelease Pool即自動(dòng)釋放池,在Autorelease Pool內(nèi)的對(duì)象,其release方法最終由編譯器自動(dòng)完成,而不需要手動(dòng)執(zhí)行旭愧。
autorelease的具體使用方法:
(1)生成并持有NSAutoreleasePool對(duì)象
(2)調(diào)用已分配對(duì)象的autorelease實(shí)例方法
(3)廢棄NSAutoreleasePool對(duì)象
那到底什么時(shí)候廢棄NSAutoreleasePool對(duì)象呢?
在Cocoa框架中,相當(dāng)于程序住循環(huán)的NSRunLoop或者在其他程序可運(yùn)行的地方,對(duì)NSAutoreleasePool對(duì)象進(jìn)行生成颅筋、持有、和廢棄處理输枯。如下圖:
NSAutoreleasePool對(duì)象的生命周期如下:
NSAutoreleasePool的創(chuàng)建有兩種方式:
在返回對(duì)象的方法中最好使用自動(dòng)釋放池釋放對(duì)象,由于在返回該對(duì)象之前不能釋放該對(duì)象,所以可以通過(guò)自動(dòng)釋放池來(lái)延遲該對(duì)象的釋放议泵。如:
如果在一個(gè)循環(huán)中產(chǎn)生了大量的臨時(shí)對(duì)象,可以在循環(huán)的內(nèi)部提供一個(gè)自動(dòng)釋放池在下一次迭代前來(lái)清除這些對(duì)象,以減少最大內(nèi)存占用桃熄。
6.ARC
ARC是編譯器特性,可以理解為xcode的功能先口。它主要用來(lái)幫用戶(hù)做內(nèi)存管理工作,優(yōu)化開(kāi)發(fā)流程。
特別注意的是它與java的垃圾回收機(jī)制不是同一個(gè)東西,它僅是xcode的一個(gè)功能而已瞳收。在ARC下碉京,RC由編譯器自動(dòng)管理,不需要手動(dòng)管理螟深。
ARC判斷標(biāo)準(zhǔn):
只要沒(méi)有強(qiáng)指針指向該對(duì)象谐宙,該對(duì)象就會(huì)被釋放。就算此時(shí)有弱指針指向該對(duì)象界弧,或者該對(duì)象還指向其他對(duì)象凡蜻,只要沒(méi)有強(qiáng)指針指向其,其仍舊被釋放垢箕。
ARC特點(diǎn):
(1)不允許調(diào)用release划栓、retain、retainCount
(2)@property的參數(shù)
*strong:成員變量是強(qiáng)指針(適用于OC對(duì)象)
*weak:成員變量是弱指針(適用于OC對(duì)象)
*assign:適用于非OC對(duì)象
(3)MRC下的retain改用strong
在實(shí)際項(xiàng)目中,某些文件需要用到release,retain方法,比如下載的第三方框架中如果有release方法,放在支持ARC的環(huán)境下會(huì)報(bào)錯(cuò)条获。這時(shí)我們可以設(shè)置這些文件不使用ARC忠荞,其他支持ARC環(huán)境的文件繼續(xù)使用ARC,相應(yīng)的方法是選擇項(xiàng)目的Build phases,在Compile Sources中選擇不需要ARC的文件,雙擊或回車(chē),在彈出的窗口中輸入-fno-objc-arc即可。同理可在MRC環(huán)境中使用-f-objc-arc使相關(guān)文件支持ARC.
ARC模式下,創(chuàng)建新對(duì)象通常由以下幾種關(guān)鍵字來(lái)限定。
(1)_strong(默認(rèn)值),由_strong修飾的為強(qiáng)指針,對(duì)象只要有強(qiáng)指針指向它就不會(huì)被銷(xiāo)毀,每當(dāng)一個(gè)強(qiáng)指針指向它钻洒,該對(duì)象的RC+1奋姿。
(2)_weak,由_weak修飾的為弱指針,弱指針指向的對(duì)象不會(huì)影響該對(duì)象的RC值,當(dāng)弱指針指向的對(duì)象被銷(xiāo)毀時(shí),該指針的值為nil(即該指針指向一個(gè)nil對(duì)象)。
(3)_unsafe_unretained,該類(lèi)型指針不會(huì)影響所指對(duì)象的RC值,當(dāng)其所指向的對(duì)象被銷(xiāo)毀時(shí),該指針的值不會(huì)變?yōu)閚il,仍保留原有的地址(即仍然指向已被釋放對(duì)象的地址,因此才會(huì)_unsafe)素标。
注意:ARC模式下仍能使用自動(dòng)釋放池称诗。