前言:當(dāng)您向一個(gè)對(duì)象發(fā)送一個(gè)autorelease消息時(shí),Cocoa就會(huì)將該對(duì)象的一個(gè)引用放入到最新的自動(dòng)釋放池。它仍然是個(gè)正當(dāng)?shù)膶?duì)象,因此自動(dòng)釋放池 定義的作用域內(nèi)的其它對(duì)象可以向它發(fā)送消息。當(dāng)程序執(zhí)行到作用域結(jié)束的位置時(shí),自動(dòng)釋放池就會(huì)被釋放,池中的所有對(duì)象也就被釋放。
1. ojc-c 是通過一種"referring counting"(引用計(jì)數(shù))的方式來管理內(nèi)存的, 對(duì)象在開始分配內(nèi)存(alloc)的時(shí)候引用計(jì)數(shù)為一,以后每當(dāng)碰到有alloc,new,copy,retain的時(shí)候引用計(jì)數(shù)都會(huì)加一, 每當(dāng)碰到release和autorelease的時(shí)候引用計(jì)數(shù)就會(huì)減一,如果此對(duì)象的計(jì)數(shù)變?yōu)榱?, 就會(huì)被系統(tǒng)銷毀.
2. NSAutoreleasePool 就是用來做引用計(jì)數(shù)的管理工作的,這個(gè)部分后面會(huì)詳細(xì)說到.
3. autorelease和release沒什么區(qū)別,只是引用計(jì)數(shù)減一的時(shí)機(jī)不同而已,autorelease會(huì)在對(duì)象的使用真正結(jié)束的時(shí)候才做引用計(jì)數(shù)減一.
4.設(shè)定項(xiàng)目編譯環(huán)境為ARC下時(shí),編譯器會(huì)幫助我們?cè)诔绦虻娜肟趍ain函數(shù)就調(diào)用NSAutoreleasePool,這樣保證程序中不調(diào)用NSAutoreleasePool,但在退出時(shí)自動(dòng)釋放
1.NSAutoreleasePool是什么?
NSAutoreleasePool實(shí)際上是個(gè)對(duì)象引用計(jì)數(shù)自動(dòng)處理器,在官方文檔中被稱為是一個(gè)類。
NSAutoreleasePool可以同時(shí)有多個(gè),它的組織是個(gè)棧,總是存在一個(gè)棧頂pool,也就是當(dāng)前pool,每創(chuàng)建一個(gè)pool,就往棧里壓一個(gè),改變當(dāng)前pool為新建的pool,然后,每次給pool發(fā)送drain消息,就彈出棧頂?shù)膒ool,改當(dāng)前pool為棧里的下一個(gè) pool。
2.NSAutoreleasePool可以用來做什么,怎么用?
NSAutoreleasePool可以在一定程度上幫助我們蘋果開發(fā)程序員管理內(nèi)存,讓我們的工作更加嚴(yán)密,簡便。
1)在ARC項(xiàng)目中,系統(tǒng)會(huì)自動(dòng)幫助我們?cè)诔绦蛑星度隢SAutoreleasePool,此為蘋果公司程序員在寫這個(gè)編譯器的時(shí)候設(shè)定的;
2)在MRC項(xiàng)目中,我們需要自己去創(chuàng)建NSAutoreleasePool類對(duì)象去幫助我們管理內(nèi)存;
3)使用應(yīng)注意:
a.在ARC項(xiàng)目中我們同樣可以創(chuàng)建NSAutoreleasePool類對(duì)象去幫助我們更精確的管理內(nèi)存問題。
b. NSAutoreleasePool的管理范圍是在NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];與[pool release];之間的對(duì)象
c..既然ARC項(xiàng)目中設(shè)置了ARC,為什么還要使用@autoreleasepool?(注意a的案例解釋)
ARC 并不是舍棄了@autoreleasepool,而是在編譯階段幫你插入必要的retain/release/autorelease的代碼調(diào)用。
所以,跟你想象的不一樣,ARC 之下依然是延時(shí)釋放的,依然是依賴于NSAutoreleasePool,跟非 ARC 模式下手動(dòng)調(diào)用那些函數(shù)本質(zhì)上毫無差別,只是編譯器來做會(huì)保證引用計(jì)數(shù)的正確性。
參考:Retain count semantics in ARC
d.NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
當(dāng)執(zhí)行[pool autorelease]的時(shí)候,系統(tǒng)會(huì)進(jìn)行一次內(nèi)存釋放,把a(bǔ)utorelease的對(duì)象釋放掉,如果沒有NSAutoreleasePool , 那這些內(nèi)存不會(huì)釋放
注意,對(duì)象并不是自動(dòng)被加入到當(dāng)前pool中,而是需要對(duì)對(duì)象發(fā)送autorelease消息,這樣,對(duì)象就被加到當(dāng)前pool的管理里了。當(dāng)當(dāng)前pool接受到drain消息時(shí),它就簡單的對(duì)它所管理的所有對(duì)象發(fā)送release消息。(如例子1)
e.在ARC項(xiàng)目中.不能直接使用autorelease pools,而是使用@autoreleasepool{},
@autoreleasepool{}比直接使用NSAutoreleasePool效率高。不使用ARC的時(shí)候也可以使用(autorelease嵌套)
4)使用例子:
例子1:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString* nsstring;
char* cstring = "Hello CString";
nsstring = [NSString stringWithUTF8String:cstring];
[pool release];(這一行代碼就是在給pool發(fā)送drain消息了)
官方API摘抄翻譯:
3.autorelease的原理是什么?
Autorelease實(shí)際上只是把對(duì)release的調(diào)用延遲了,對(duì)于每一個(gè)Autorelease,系統(tǒng)只是把該Object放入了當(dāng) 前的Autorelease pool中,當(dāng)該pool被釋放時(shí),該pool中的所有Object會(huì)被調(diào)用Release。
4.!!!autorelease何時(shí)釋放?
對(duì)于autorelease pool本身,會(huì)在如下兩個(gè)條件發(fā)生時(shí)候被釋放(詳細(xì)信息請(qǐng)參見第5條)
1)、手動(dòng)釋放Autorelease pool
2)、Runloop結(jié)束后自動(dòng)釋放
對(duì)于autorelease pool內(nèi)部的對(duì)象在引用計(jì)數(shù)的retain == 0的時(shí)候釋放。release和autorelease pool 的 drain都會(huì)觸發(fā)retain--事件。
5、autorelease釋放的具體原理是什么?
要搞懂具體原理,則要先要搞清楚autorelease何時(shí)會(huì)創(chuàng)建。
我們的程序在main()調(diào)用的時(shí)候會(huì)自動(dòng)調(diào)用一個(gè)autorelease,然后在每一個(gè)Runloop, 系統(tǒng)會(huì)隱式創(chuàng)建一個(gè)Autorelease pool,這樣所有的release pool會(huì)構(gòu)成一個(gè)象CallStack一樣的一個(gè)棧式結(jié)構(gòu),在每一個(gè)Runloop結(jié)束時(shí),當(dāng)前棧頂?shù)?Autorelease pool(main()里的autorelease)會(huì)被銷毀,這樣這個(gè)pool里的每個(gè)Object會(huì)被release。
可以把a(bǔ)utorelease pool理解成一個(gè)類似父類與子類的關(guān)系,main()創(chuàng)建了父類,每個(gè)Runloop自動(dòng)生成的或者開發(fā)者自定義的autorelease pool都會(huì)成為該父類的子類。當(dāng)父類被釋放的時(shí)候,沒有被釋放的子類也會(huì)被釋放,這樣所有子類中的對(duì)象也會(huì)收到release消息。
那什么是一個(gè)Runloop呢? 一個(gè)UI事件,Timer call, delegate call, 一個(gè)鼠標(biāo)事件,鍵盤按下(MAC OSX),或者iphone上的觸摸事件,異步http連接下后當(dāng)接收完數(shù)據(jù)時(shí),都會(huì)是一個(gè)新的Runloop。
一般來說,消息循環(huán)運(yùn)行一次是毫秒級(jí)甚至微秒級(jí)的,因此autorelease的效率仍然是非常高的,確實(shí)是一個(gè)巧妙的設(shè)計(jì)。
6、使用有什么要注意的?
1)、NSAutoreleasePool可以創(chuàng)建一個(gè)autorelease pool,但該對(duì)象本身也需要被釋放,如:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init;
// Code benefitting from a local autorelease pool.
[pool release];
復(fù)制代碼
在引用計(jì)數(shù)環(huán)境下,使用[pool release]或[pool drain]效果是相同的,drain僅適用于max os高版本,低版本不適用,而release通用,其它并無太大差別。
2)、在ARC下,不能使用上述方式調(diào)用autorelease,而應(yīng)當(dāng)使用@autoreleasepool,如:
@autoreleasepool {
// Code benefitting from a local autorelease pool.
}
復(fù)制代碼
3)、盡量避免對(duì)大內(nèi)存使用該方法,如圖片。對(duì)于這種延遲釋放機(jī)制,還是盡量少用,最好只用在方法內(nèi)返回小塊內(nèi)存申請(qǐng)地址值的情況下,且參考和領(lǐng)會(huì)OC的一些系統(tǒng)方法,如:[NSString stringWithFormat:]。
4)、不要把大量循環(huán)操作放到同一個(gè)NSAutoreleasePool之間,這樣會(huì)造成內(nèi)存峰值的上升。
7、關(guān)于多線程,有什么要注意的?
我還未實(shí)際使用到,在官方API翻譯出類似如下語句:
1)、對(duì)于不同線程,應(yīng)當(dāng)創(chuàng)建自己的autorelease pool。如果應(yīng)用長期存在,應(yīng)該定期drain和創(chuàng)建新的autorelease pool
下面這句話摘自官方API,大概是說多線程中如果沒有使用到cocoa的相關(guān)調(diào)用,則不需要?jiǎng)?chuàng)建autorelease pool,我一直沒有理解透徹
If, however, your detached thread does not make Cocoa calls, you do not need to create an autorelease pool.
2)、如果不是使用的NSThread,就不要用aoturelease pool,除非你是多線程模式(multithreading mode) ,可以使用NSThread的isMultiThreaded方法測(cè)試你的應(yīng)用是否是多線程模式
PS:
我把它理解為:新開線程最好實(shí)現(xiàn)NSAutoreleasePool(當(dāng) 我們點(diǎn)擊一個(gè)App中的一個(gè)按鈕或者其他可以觸碰開啟新業(yè)務(wù)的UI控件,在程序里面就會(huì)自動(dòng)開啟一條新線程,當(dāng)我們不用這個(gè)業(yè)務(wù)的時(shí)候,就需要程序幫我們 提前在“程序的主窗口的所有代碼編譯結(jié)束后”之前關(guān)閉這條線程以優(yōu)化線程的使用,一定程度上盡量避免線程開啟太多占用CPU嚴(yán)重引起的卡頓問題)(業(yè)務(wù)邏 輯處理-業(yè)務(wù)線程優(yōu)化)