概述
無(wú)論是ios還是android中辕狰,系統(tǒng)對(duì)每個(gè)程序運(yùn)行時(shí)內(nèi)存的占用都有一個(gè)限制,默認(rèn)都是幾十M左右大小灿渴,當(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)存有限定拟,所以我們需要對(duì)內(nèi)存進(jìn)行嚴(yán)格的管理,以避免內(nèi)存泄露造成資源浪費(fèi)逗嫡。在OC中青自,只有對(duì)象才屬于內(nèi)存管理范圍,例如int驱证、struce等基本數(shù)據(jù)類(lèi)型不存在內(nèi)存管理的概念性穿。在iOS開(kāi)發(fā)中,對(duì)內(nèi)存的管理實(shí)際上就是對(duì)引用計(jì)數(shù)器的管理雷滚。
相關(guān)概念:
僵尸對(duì)象:所占用的內(nèi)存已經(jīng)被回收的對(duì)象,僵尸對(duì)象不能再使用
野指針:指向僵尸對(duì)象的指針吗坚,給野指針發(fā)送消息會(huì)報(bào)錯(cuò)
空指針:沒(méi)有指向任何東西的指針(存儲(chǔ)的東西是nil祈远、null、0)商源,給空指針發(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ù)器)庄吼。
下面對(duì)這三種方式進(jìn)行分別介紹缎除。
一、自動(dòng)垃圾收集
在OC2.0中总寻,有一種自動(dòng)垃圾收集的內(nèi)存管理形式器罐,通過(guò)垃圾自動(dòng)收集,系統(tǒng)能夠自動(dòng)檢測(cè)出對(duì)象是否擁有其他的對(duì)象渐行,當(dāng)程序運(yùn)行期間轰坊,不被引用的對(duì)象就會(huì)自動(dòng)釋放铸董。
但是在iOS運(yùn)行環(huán)境中不支持自動(dòng)垃圾收集,在OS X環(huán)境才支持肴沫,不過(guò)Apple現(xiàn)在不建議使用該方法粟害,而是推薦使用ARC進(jìn)行替代。
二颤芬、MRC和自動(dòng)釋放池
1悲幅、引用計(jì)數(shù)器的概念:
引用計(jì)數(shù)器即一個(gè)對(duì)象被引用(使用)的次數(shù),每個(gè)對(duì)象的引用計(jì)數(shù)器占用4個(gè)字節(jié)驻襟。
如下圖所示夺艰,當(dāng)使用A創(chuàng)建一個(gè)對(duì)象object的時(shí)候,object的RC(引用計(jì)數(shù)器值)默認(rèn)為1沉衣,當(dāng)B也指向object的時(shí)候郁副,object的RC+1=2。然后A指針不指向object的時(shí)候豌习,object的RC-1=2-1=1存谎。最后當(dāng)B也不指向object的時(shí)候,object的RC-1=1-1=0肥隆,此時(shí)對(duì)象object被銷(xiāo)毀既荚。
說(shuō)明: 當(dāng)一個(gè)對(duì)象被創(chuàng)建的時(shí)候,該對(duì)象的RC默認(rèn)為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件蚕、引用計(jì)數(shù)器的操作:
(1)給對(duì)象發(fā)送一條retain消息孙技,可以使RC+1(retain方法返回對(duì)象本身)
(2)給對(duì)象發(fā)送一條release消息,可以使RC-1
(3)給對(duì)象發(fā)送一條retainCount消息排作,可以獲得當(dāng)前的引用計(jì)數(shù)器值
3牵啦、對(duì)象的銷(xiāo)毀:
(1)當(dāng)一個(gè)對(duì)象的RC為0時(shí),那么它將被銷(xiāo)毀纽绍,其占用的內(nèi)存會(huì)被系統(tǒng)回收蕾久。
(2)當(dāng)一個(gè)對(duì)象被銷(xiāo)毀時(shí),系統(tǒng)會(huì)自動(dòng)向?qū)ο蟀l(fā)送一條dealloc消息
(3)一般會(huì)重寫(xiě)dealloc方法,在這里釋放相關(guān)資源僧著,dealloc就像對(duì)象的遺言履因。
(4)一旦重寫(xiě)了dealloc方法,就必須調(diào)用[super dealloc]盹愚,并且放在最后面調(diào)用栅迄。
(5)不要直接調(diào)用dealloc方法。
(6)一旦對(duì)象被回收了皆怕,它占用的內(nèi)存不再可用毅舆,堅(jiān)持使用就會(huì)導(dǎo)致程序崩潰(指針錯(cuò)誤)。
4愈腾、MRC
MRC即我們通過(guò)人為的方式來(lái)控制引用計(jì)數(shù)器的增減憋活,影響對(duì)象RC值得方法有以下幾種:
(1)new、alloc虱黄、copy悦即、mutableCopy,這幾種方法用來(lái)創(chuàng)建一個(gè)新的對(duì)象并且獲得對(duì)象的所有權(quán)橱乱,此時(shí)RC的值默認(rèn)為RC=1辜梳;
(2)retain,對(duì)象調(diào)用retain方法,該對(duì)象的RC+1;
(3)release泳叠,對(duì)象調(diào)用 release方法作瞄,該對(duì)象的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ì)象)糠赦,那么將直接執(zhí)行賦值操作会傲;如果使用retain(一般用于OC對(duì)象),那么將retain新值,release舊值拙泽;如果使用copy淌山,那么將release舊值,copy新值顾瞻。不顯示使用assign為默認(rèn)值泼疑。
(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方法个粱。不顯示使用readwrite為默認(rèn)值。
(4)getter和setter
這兩個(gè)關(guān)鍵字用于給設(shè)值和取值方法另外起一個(gè)名字翻翩。例如@property(getter=a,setter=b:) int age;相當(dāng)于取值方法名為a都许,設(shè)值方法名為b:。
如果使用@property屬性嫂冻,那么上面代碼可以改為:
循環(huán)引用內(nèi)存管理原則
對(duì)于兩個(gè)類(lèi)A包含B胶征,B包含A的循環(huán)引用情況下,看如下代碼:
下面分析主函數(shù)代碼桨仿,當(dāng)執(zhí)行Book1 *b1=[[Book1 alloc] init]后睛低,b1指向Book1。當(dāng)執(zhí)行Book2 *b2=[[Book2 alloc] init]后蹬敲,b2指向Book2暇昂。 當(dāng)執(zhí)行b1.book2 = b2后,Book1的成員變量_b2指向Book2伴嗡。當(dāng)執(zhí)行b2.book1 = b1后急波,Book2的成員變量_b1指向Book1。內(nèi)存中具體關(guān)系如下圖所示瘪校。
此時(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窃蹋,這時(shí)內(nèi)存中Book1和Book2的關(guān)系如黑色橢圓內(nèi)所示卡啰。所以并不會(huì)調(diào)用dealloc函數(shù),所以Book1和Book2并不會(huì)毀銷(xiāo)警没,這樣就造成了 內(nèi)存泄露匈辱。
對(duì)于上面這種情況,只需要在Book1和Book2的@property屬性聲明中一端使用retain杀迹,一端使用assign亡脸。即將@property(nonatomic,retain) Book1 *_book1或者@property(nonatomic,retain) Book2 *_book2中的一個(gè)retian改為assign。具體原因自己分析。
對(duì)于下面這種循環(huán)引用情況浅碾,只能使用assign大州。
大家可以分析一下,如果@property(nonatomic,assign)id instance; 中將assign換為retain及穗,那么也將造成內(nèi)存泄露摧茴。
5、Autorelease Pool的使用
顧名思義埂陆,autorelease即自動(dòng)釋放對(duì)象苛白,不需要我們手動(dòng)釋放。從上面代碼我們知道焚虱,在主函數(shù)中购裙,創(chuàng)建對(duì)象obj后,總要手動(dòng)調(diào)用[obj release]方法鹃栽,這樣無(wú)疑使工作量變大躏率,且對(duì)我們的技術(shù)增長(zhǎng)毫無(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ì)象在創(chuàng)建時(shí)只要調(diào)用了autorelease方法耍贾,那么在該池子內(nèi)的對(duì)象的最后的release方法的調(diào)用將由編譯器完成。
如下圖:
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)行生成简肴、持有晃听、和廢棄處理。因此砰识,開(kāi)發(fā)者不一定非得使用NSAutoreleasePool對(duì)象來(lái)進(jìn)行開(kāi)發(fā)工作杂伟。如下圖:
NSAutoreleasePool對(duì)象的生存周期如下:
根據(jù)Apple文檔的建議,如果在一個(gè)循環(huán)中產(chǎn)生了大量的臨時(shí)對(duì)象仍翰,可以通過(guò)在循環(huán)內(nèi)部提供一個(gè)自動(dòng)釋放池在下一次迭代之前來(lái)清除這些對(duì)象,以減少最大內(nèi)存占用观话。
autorelease錯(cuò)誤用法:
(1) alloc之后調(diào)用了autorelease予借,又調(diào)用release;
(2) 連續(xù)調(diào)用多次autorelease;
注意:
(1)release方法不能多次調(diào)用灵迫,該調(diào)用的時(shí)候調(diào)用秦叛,否則容易造成野指針錯(cuò)誤。
(2)創(chuàng)建對(duì)象時(shí)多次調(diào)用autorelease方法瀑粥,容易造成野指針錯(cuò)誤挣跋。
(3)在自動(dòng)釋放池中新創(chuàng)建的對(duì)象并不是一定會(huì)添加到釋放池中,例如由new狞换、alloc避咆、copy、mutableCopy創(chuàng)建的對(duì)象并不會(huì)加到自動(dòng)釋放池中修噪,并且必須手動(dòng)調(diào)用release方法才能釋放對(duì)象查库。如果想讓新創(chuàng)建的對(duì)象加入到自動(dòng)釋放池中,就必須調(diào)用autorelease方法黄琼。
(4)使用autorelease方法并不會(huì)使引用計(jì)數(shù)器的值增加樊销,只是表示將該對(duì)象加入到自動(dòng)釋放池中。
三脏款、ARC
ARC是編譯器特性围苫,可以理解為編譯器xcode的功能。它主要用來(lái)幫助用戶(hù)做內(nèi)存管理工作撤师,優(yōu)化開(kāi)發(fā)流程剂府。特別注意的是它與java的垃圾回收機(jī)制不是同一個(gè)東西,它只是xcode的一個(gè)功能而已丈氓!在ARC下周循,RC將由編譯器來(lái)自動(dòng)完成對(duì)象引用計(jì)數(shù)器的控制,不需要手動(dòng)完成万俗。
1湾笛、ARC判斷準(zhǔn)則:
只要沒(méi)有強(qiáng)指針指向?qū)ο螅蜁?huì)釋放對(duì)象闰歪。就算此時(shí)仍有弱指針指向該對(duì)象或者該對(duì)象還指向其他對(duì)象嚎研,只要沒(méi)有強(qiáng)指針指向它就會(huì)釋放對(duì)象。系統(tǒng)還會(huì)根據(jù)弱指針的情況及時(shí)釋放弱指針對(duì)象库倘,避免野指針的產(chǎn)生临扮。
指針?lè)謨煞N:
(1)強(qiáng)指針:默認(rèn)情況下,所有指針都是強(qiáng)指針 (_strong), 也可將strong作為參數(shù)傳給@property
(2)弱指針:定義一個(gè)指針是弱指針教翩,只需在定義時(shí)用_weak聲明即可杆勇,比如:_weak Person *p = [[Person alloc] init];也可將weak作為參數(shù)傳給@property饱亿。
2蚜退、ARC特點(diǎn):
(1) 不允許調(diào)用release闰靴、retain、retainCount
(2) 允許重寫(xiě)dealloc钻注,但是不允許調(diào)用[super dealloc]
(3) @property的參數(shù)
* strong :成員變量是強(qiáng)指針(適用于OC對(duì)象類(lèi)型)
* weak :成員變量是弱指針(適用于OC對(duì)象類(lèi)型)
* assign : 適用于非OC對(duì)象類(lèi)型
(4) 以前MRC下的retain改為用strong
【備注】在實(shí)際項(xiàng)目中有這種需求蚂且,某些文件需要用到release、retain方法幅恋,比如下載的第三方框架中如果有release等方法杏死,放到支持ARC的編譯環(huán)境中肯定會(huì)報(bào)錯(cuò)。這時(shí)我們可以設(shè)置這些文件不使用ARC而項(xiàng)目中其他文件繼續(xù)使用ARC捆交,方法很簡(jiǎn)單淑翼,選擇項(xiàng)目的Bulild phases,在Compile Sources中選擇不需要ARC的文件零渐,雙擊或按回車(chē)窒舟,在彈出的窗口中輸入-fno-objc-arc即可。同樣也可以在非ARC的環(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)指針指向一個(gè)對(duì)象戒财,該對(duì)象的的RC+1热监;
(2)__weak,由__weak修飾的為弱指針,弱指針?biāo)赶虻膶?duì)象并不會(huì)改變RC值饮寞,弱指針只表示是對(duì)對(duì)象的引用孝扛;當(dāng)弱指針?biāo)赶虻膶?duì)象銷(xiāo)毀時(shí),該弱指針的值變?yōu)閚il幽崩;
(3)__unsafe_unretained,__unsafe_unretained修飾的對(duì)象指針?biāo)赶虻膶?duì)象也不會(huì)改變RC值苦始,也只表示是對(duì)對(duì)象的引用;當(dāng)所指向的對(duì)象銷(xiāo)毀時(shí)慌申,該指針的值不會(huì)變?yōu)閚il陌选,仍是保留原有的地址;
在ARC模式下蹄溉,MRC中的retain咨油、release等方法變的不可用,因?yàn)锳RC是不需要我們手動(dòng)管理內(nèi)存的柒爵,一切由編譯器完成役电。
MRC模式下,將一個(gè)對(duì)象指針賦值給另一個(gè)對(duì)象指針如下:
3棉胀、ARC模式下的循環(huán)引用
在ARC模式下法瑟,@property屬性關(guān)于內(nèi)存管理的修飾符為strong和weak(MRC下的retain和assign不可用)囱晴,表示聲明為強(qiáng)指針還是弱指針。通常情況下都是使用strong來(lái)修飾瓢谢,但是在循環(huán)引用卻不是。
下面這種情況一端使用strong修飾驮瞧,一端使用weak修飾氓扛。如果都使用strong修飾,那么將造成對(duì)象的循環(huán)保持论笔,造成內(nèi)存泄漏采郎。
注意:
(1)ARC模式下仍能使用自動(dòng)釋放池;
(2)MRC下的retain狂魔、release蒜埋、retainCount、autorelease等方法在ARC下不可使用最楷。
(3)注意循環(huán)引用下strong和weak的選擇整份。