Objective-C對象的生命期取決于其引用的計數(shù)羡宙。在Objective-C的引用計數(shù)構(gòu)架中汽抚,有一種特征叫做“自動釋放池塊”(autorelease pool)殊轴。釋放隊(duì)形有兩個方式:以后總是調(diào)用release方式,時期保留計數(shù)立即遞減:另一種是調(diào)用autorelease方法财边,將其加入”自動釋放池“時氧苍,系統(tǒng)會將其中多個對象發(fā)送release消息夜矗。
自動釋放池的原理.
存入到自動釋放池中的對象,在自動釋放池被銷毀的時候.會自動調(diào)用存儲在該自動釋放池中的所有對象的release方法.
可以解決的問題:
將創(chuàng)建的對象,存入到自動釋放池之中. 就不再需要手動的relase這個對象了.
因?yàn)槌刈愉N毀的時候 就會自動的調(diào)用池中所有的對象的relase。
創(chuàng)建自動釋放池的語法運(yùn)用如下:
@autoreleasepool {
//.......
}
如果在沒有創(chuàng)建自動釋放池的情況下給對象發(fā)送autorelease消息让虐,那么控制臺就會輸出這樣一條消息:
Objective 0xabcd0123 of class __NSCFString autorelease
with no pool in place - just leaking - break on objc
autoreleaseNoPool() to doing
然而紊撕,一般情況下無需擔(dān)心自動釋放池的創(chuàng)建問題。Mac OS X 與iOS應(yīng)用程序分別運(yùn)用于Cocoa及Cocoa Touch環(huán)境中個赡突。系統(tǒng)和會自動創(chuàng)建一些路線对扶,比如說主線或者”大中樞派發(fā)“(Grand Central Dispatch,GCD)機(jī)制中的線程,這些線程默認(rèn)都是自動釋放惭缰,每次執(zhí)行”事件循環(huán)“時浪南,就會進(jìn)將其清空。因此漱受,不需要自己來創(chuàng)建“自動釋放吃”同塵個只有一個地方需要創(chuàng)建自動釋放池络凿,那就在main函數(shù)里。我們用自動釋放池來包裹應(yīng)用程序的主入口。比方說絮记。iOS程序的面函數(shù)經(jīng)常這樣寫:
int main(int argh, char *argv[]) {
@autoreleasepool {
return UIApplicationMain(argh,argv,nil,@"EOCAppDelegate");
}
}
從技術(shù)角度來看摔踱,不是非得有個“自動釋放池”才行。應(yīng)為塊的末尾敲好就是應(yīng)用程序的終結(jié)處怨愤,二此時操作系統(tǒng)會把程序所占的全部內(nèi)存全都釋放掉派敷,雖然如此,但是如果不寫這個塊的話撰洗,那么有UIApplicationMain函數(shù)所自動釋放的那些對象篮愉,就沒有自動釋放池可以容納了,于是西戎會發(fā)出警告信息來表明這一情況了赵。所以說,這個吃可以理解成最外圍捕捉自動釋放對象所用的池甸赃。
下面這段代碼中的話括號定義了自動化四方吃的范圍柿汛。自動釋放池于左括號處創(chuàng)建,并于對應(yīng)右括號處清空埠对。位于自動釋放池中的對象络断,將在此范圍末尾處收到release的消息。自動釋放吃可以嵌套项玛。系統(tǒng)在自動該釋放對象的時候貌笨,會把它放到最內(nèi)層的池里。比方說:
@autoreleasepool {
NSString *string = [NSString stringWithFormat:@"1 - %i",1];
@autoreleasepool {
NSNumber *number = [NSNumber numberWithInt:1];
}
}
使用注意
只有在自動釋放池中調(diào)用了對象的autorelease方法,這個對象才會被存儲到這個自動釋放池之中.
如果只是將對象的創(chuàng)建代碼寫在自動釋放之中,而沒有調(diào)用對象的autorelease方法.是不會將這個對象存儲到這個自動釋放池之中的.
對象的創(chuàng)建可以在自動釋放池的外面,在自動釋放池之中,調(diào)用對象的autorelease方法,就可以將這個對象存儲到這個自動釋放池之中.
如果對象的autorelease方法的調(diào)用放在自動釋放池的外面,是無法將其存儲的這個自動釋放池之中的.
autorelease 的調(diào)用只有放在自動釋放池之中 才可以講其存儲到自動釋放池之中, 對象的創(chuàng)建可以在外面
當(dāng)自動釋放池結(jié)束的時候.僅僅是對存儲在自動釋放池中的對象發(fā)送1條release消息 而不是銷毀對象.
如果在自動釋放池中,調(diào)用同1個對象的autorelease方法多次.就會將對象存儲多次到自動釋放池之中.
在自動釋放池結(jié)束的時候.會為對象發(fā)送多條release消息.
所以,1個自動釋放池之中,只autorelease1次,只將這個對象放1次, 否則就會出現(xiàn)野指針錯誤.
如果在自動釋放池中,調(diào)用了存儲到自動釋放中的對象的release方法.
在自動釋放池結(jié)束的時候,還會再調(diào)用對象的release方法.
這個時候就有有可能會造成野指針操作.
將對象存儲到自動釋放池,并不會使對象的引用計數(shù)器+1 所以其好處就是:創(chuàng)建對象將對象存儲在自動釋放池,就不需要在寫個release了.
自動釋放池可以嵌套.
調(diào)用對象的autorelease方法,會講對象加入到當(dāng)前自動釋放池之中
只有在當(dāng)前自動釋放池結(jié)束的時候才會像對象發(fā)送release消息.
autorelease的應(yīng)用場景.
創(chuàng)建對象,將對象存儲到自動釋放池之中. 就不需要再去手動的realse锥惋。
我們一般情況下,會為我們的類寫1個類方法,用來讓外界調(diào)用類方法來快速的得到1個對象.
規(guī)范:使用類方法得到的對象,要求這個對象就已經(jīng)被autorelease過了.
提供1個類方法來快速的得到1個對象.
規(guī)范
這個類方法以類名開頭. 如果沒有參數(shù)就直接是類名 如果有參數(shù)就是 類名WithXX:
使用類方法得到的對象,要求這個對象就已經(jīng)被autorelease過了.
- (instancetype)person
{
return [[[self alloc] init] autorelease];
}
以下是我整理的需要注意的事項(xiàng):
(1)在自動釋放池@autoreleasepool{}中alloc一個對象后,仍然需要用[p1 autorelease];只是這個語句和[p1 release];不同开伏,后者表示把p1的retainCount-1膀跌,而前者僅僅表示把p1放到自動釋放池中返回一個self,自動釋放池結(jié)束銷毀時固灵,統(tǒng)一對里面的對象引用計數(shù)retainCount-1捅伤。
(2)@autoreleasepool{}可以隨意創(chuàng)建,也可以嵌套使用巫玻。
(3)不管這個對象是在自動釋放池內(nèi)還是外創(chuàng)建的丛忆,只要在自動釋放池內(nèi)寫一個[p1 autorelease];p1就會被放到自動釋放池中。注意autorelease是一個方法仍秤,且只有在自動釋放池中使用才有效熄诡。
(4)如果把一個對象重復(fù)加到自動釋放池如[p1 autorelease];[p1 autorelease];,那么會出錯诗力。原因是:加載幾次粮彤,屆時自動釋放池就會用[p1 release];釋放幾次,但是由于這兩個加載的對象其實(shí)是一個對象同樣地址,所以第一次自動釋放正確导坟,第二次自動釋放時發(fā)現(xiàn)已經(jīng)被釋放了屿良,所以p1就變成了野指針。
(5)以下是自動釋放池嵌套的使用規(guī)則和注意點(diǎn)惫周。
[objc] view plain copy 在CODE上查看代碼片派生到我的代碼片
import <Foundation/Foundation.h>
import "Person.h"
int main(int argc, const charchar * argv[]) {
Person *p1=[[Person alloc]init];
@autoreleasepool {
@autoreleasepool {
[p1 autorelease];
}//在執(zhí)行到此處時尘惧,p1被自動釋放
}
//以下代碼有錯誤
@autoreleasepool {
[p1 autorelease];//此時p1被加入進(jìn)來
@autoreleasepool {
[p1 autorelease];//被重復(fù)加載進(jìn)來,但仍然同一個
}//此處递递,p1被自動釋放了喷橙,所以第一次加進(jìn)來的那個也被釋放了,因?yàn)槭峭粋€對象
}//所以此處在調(diào)用[p1 release];時就出現(xiàn)報錯:野指針
return 0;
}
(6)@autoreleasepool的應(yīng)用:如果需要在方法中創(chuàng)建對象登舞,并把這個對象作為返回值贰逾,那么可以在這個方法中使用[*** autorelease];把它加入到自動釋放池中,否則菠秒,直接用[*** release];來匹配alloc的話疙剑,在該方法中就已經(jīng)把這個對象alloc和release了一遍相當(dāng)于釋放了,那么所謂的返回對象返回的時一個野指針(沒有指向任何對象)践叠。當(dāng)然言缤,調(diào)用這個方法的代碼頁需要寫在自動釋放池作用域內(nèi)才生效。
(7)接上面禁灼。返回對象的那個方法中管挟,創(chuàng)建對象不建議直接用類名,而是用self弄捕,否則如果存在子類調(diào)用會崩潰僻孝。如Car *car1=[[self alloc]init];
(8)其實(shí)諸如NSString *str1=[NSString stringWithFormat:@"%@",@"hello"];也是調(diào)用了一個方法,并且返回了一個字符串對象守谓。比照(6)和(7)我們得知這個stringWithFormat應(yīng)該也是順便返回了一個autorelease皮璧。
(9)在ARC機(jī)制中,我們用@property聲明的成員變量分飞,建議用strong代替之前手動管理內(nèi)存時的retain悴务,雖然后者仍然可以使用。因?yàn)槲覀冊贏RC中內(nèi)存管理就是看是否有強(qiáng)指針指向?qū)ο笃┟ǎ缬芯筒换厥昭堕埽鐩]有就回收。所以強(qiáng)指針是strong染服,相反是weak别洪。而基本數(shù)據(jù)類型我們還是習(xí)慣用assign。
(10)雖然Xcode提供了非ARC轉(zhuǎn)換成ARC的柳刮,很少有把整個非ARC轉(zhuǎn)換成ARC的挖垛。如果我們導(dǎo)入第三方庫時痒钝,需要非ARC和ARC共存,即我們系統(tǒng)默認(rèn)是ARC痢毒,我們需要讓系統(tǒng)不要去管這個非ARC的第三方庫送矩,如下設(shè)置:雙擊響應(yīng)的.m文件,輸入-fno-objc-arc回車即可哪替。
(11)順便栋荸,當(dāng)出現(xiàn)兩個類循環(huán)引用的話(也就是A要包含B,B要包含A凭舶,即A對象要作為B的變量晌块,B對象要作為A的變量),只需要把一方的strong改成weak帅霜,并且在響應(yīng)的.h文件中把#import ".h"改成Class ***匆背。如果因?yàn)楦某蒀lass ***而無法使用那個類的方法的話,只需要在它的.m文件中#import“.h”文件即可身冀,這個因?yàn)椴皇窃?h文件中導(dǎo)入所以不沖突钝尸。
這就是我整理的關(guān)于Autorelease的使用以及“自動釋放池塊”降低內(nèi)存峰值的使用方法