iPhone系統(tǒng)中的Objective-C的內(nèi)存管理機(jī)制是比較靈活的,即可以拿來像C/C++一樣用,也可以加個(gè)AutoreleasePool讓它升級(jí)為半自動(dòng)化的內(nèi)存管理語言。當(dāng)然,也不能拿JAVA虛擬機(jī)中的全自動(dòng)化GC來比〜
一,引用計(jì)數(shù)是實(shí)例對(duì)象的內(nèi)存回收唯一參考
引用計(jì)數(shù)(retainCount)是Objective-C管理對(duì)象引用的唯一依據(jù)。調(diào)用實(shí)例的release方法后,此屬性減一,減到為零時(shí)對(duì)象的dealloc方法被自動(dòng)調(diào)用,進(jìn)行內(nèi)存回收操作,也就是說我們永不該手動(dòng)調(diào)用對(duì)象的dealloc方法。
它的內(nèi)存管理API老簡(jiǎn)單老簡(jiǎn)單了,下面就是它主要操作接口:
1,alloc, allocWithZone,new(帶初始化)
為對(duì)象分配內(nèi)存,retainCount為“1”,并返回此實(shí)例
2,release
retainCount 減“1”,減到“0”時(shí)調(diào)用此對(duì)象的dealloc方法
3,retain
retainCount 加“1”
4,copy,mutableCopy
復(fù)制一個(gè)實(shí)例,retainCount數(shù)為“1”,返回此實(shí)例。所得到的對(duì)象是與其它上下文無關(guān)的,獨(dú)立的對(duì)象(干凈對(duì)象)。
5,autorelease
在當(dāng)前上下文的AutoreleasePool棧頂?shù)腶utoreleasePool實(shí)例添加此對(duì)象,由于它的引入使Objective-C(非GC管理環(huán)境)由全手動(dòng)內(nèi)存管理上升到半自動(dòng)化。
二,Objective-C內(nèi)存管理準(zhǔn)則
我們可以把上面的接口按對(duì)retainCount的操作性質(zhì)歸為兩類,
A類是加一操作:1,3,4
B類是減一操作:2,5(延時(shí)釋放)
內(nèi)存管理準(zhǔn)則如下:
1,A與B類的調(diào)用次數(shù)保持一制
2,為了很好的保障準(zhǔn)則一,以實(shí)例對(duì)象為單位,誰A了就誰B,沒有第二者參與
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSObject *o = [[NSObject alloc] init]; //retainCount為1
[o retain]; //retainCount為2
[o release]; //retainCount為1
[o autorelease]; //retainCount為1
[pool release]; //retaincount為0,觸發(fā)dealloc方法
三,對(duì)象的擁有者
面向?qū)ο箢I(lǐng)域里有個(gè)引用的概念,區(qū)別于繼承,引用常被用來當(dāng)做偶合性更小的設(shè)計(jì)。繼承是強(qiáng)依賴,對(duì)吧。我們要降偶軟件的設(shè)計(jì),就要盡量減少對(duì)它的使用。但沒有任何偶合的模塊或功能是沒有用的〜對(duì)吧,那我們只能多用引用了吧。一個(gè)實(shí)例擁有另一個(gè)實(shí)例的時(shí)候,我們稱它為引用了另一個(gè)實(shí)例。
比如ClassA類的一個(gè)屬性對(duì)象的Setter方法:
- (void)setMyArray:(NSMutableArray *)newArray {
if (myArray != newArray) {
[myArray release];
myArray = [newArray retain];
}
}
假設(shè)這個(gè)類的一個(gè)實(shí)例為'a',調(diào)用setMyArray后,我們就可以說a擁有了一個(gè)新的myArray實(shí)例,也可以說a引用了一個(gè)新的myArray實(shí)例。其中調(diào)用的retain方法,使myArray的retainCount加一,我們需要注意以下兩個(gè)地方:
1,setMyarray方法中,在retain之前先release了舊實(shí)例一次
2,在本實(shí)例的dealloc方法中,本應(yīng)該是要再次release當(dāng)前實(shí)例的,但回頭看看參考內(nèi)存管理準(zhǔn)則。它并不合理,對(duì)吧。。。多了一次release。這里比較推薦的做法是:
[myArray setMyArray:nil];
這樣可以巧妙的使當(dāng)前實(shí)例release而不出錯(cuò)(我們可以向nil發(fā)送消息〜其實(shí)它本身就是個(gè)整數(shù)0),并符合我們的內(nèi)存管理準(zhǔn)則。更主要的是,很簡(jiǎn)單,你不需要考慮過多的事情。
另外一個(gè)比較容易忽略而又比較經(jīng)典的問題是實(shí)例變量的循環(huán)引用,Objective-C為此區(qū)分了,其實(shí)也相當(dāng)相當(dāng)?shù)暮?jiǎn)單:
1,強(qiáng)引用,上面講的就是強(qiáng)引用,存在retainCount加一。
2,弱引用,但凡是assign聲明并直接用指針賦值實(shí)現(xiàn)的被稱之為弱引用,不存在retainCount加一的情況。
四,AutoreleasePool使Objective-C成為內(nèi)存管理半自動(dòng)化語言
如果僅僅是上面這些,很簡(jiǎn)單,對(duì)吧。但往往很多人都會(huì)迷糊在自動(dòng)內(nèi)存管理這塊上,感覺像是有魔法,但其實(shí)原理也很簡(jiǎn)單〜
先看看最經(jīng)典的程序入口程序:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
我們先把pool看成一個(gè)普通對(duì)象〜很簡(jiǎn)單,先是alloc,pool的retainCount為1。第三句release,retainCount為0,自動(dòng)調(diào)用它的dealloc方法。它和任何其它普通對(duì)象沒 任何區(qū)別。
魔法在哪里?
在聲明pool后,release它之前的這段代碼,所有段里的代碼(先假設(shè)中間沒有聲明其它的AutoreleasePool實(shí)例),凡是調(diào)用了autorelase方法的實(shí)例,都會(huì)把它的retainCount加1,并在此pool實(shí)例中添1次此實(shí)例要回收的記錄以做備案。當(dāng)此pool實(shí)例dealloc時(shí),首先會(huì)檢查之前備案的所有實(shí)例,所有記錄在案的實(shí)例都會(huì)依次調(diào)用它的release方法。
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSObject *o = [[NSObject alloc] init];
[o autorelease]; //在pool實(shí)例dealloc時(shí),release一次此實(shí)例,重要的是并不是在此行去release
NSLog(@"o retainCount:%d",[o retainCount]); //此時(shí)還可以看到我們的o實(shí)例還是可用的,并且retainCount為1
[pool release]; //pool 的 retainCount為0,自動(dòng)調(diào)用其dealloc方法,我們之前備案的小o也將在這里release一次(因?yàn)樵蹅冎皟H僅autorelease一次)
真對(duì)同一個(gè)實(shí)例,同一個(gè)Pool是可以多次注冊(cè)備案(autorelease)的。在一些很少的情況化可能會(huì)出現(xiàn)這種需求:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSObject *o = [[NSObject alloc] init];
[o retain];
[o autorelease];
[o autorelease];
[pool release];
我們調(diào)用了兩次A類(retainCount加1的方法),使其retainCount為2,而接下來的兩次autorelease方法調(diào)用,使其在pool中注冊(cè)備案了兩次。這里的pool將會(huì)在回收時(shí)調(diào)用此實(shí)例的兩次release方法。使其retainCount降為0,完成回收內(nèi)存的操作,其實(shí)這也是完全按照內(nèi)存管理規(guī)則辦事的好處〜
AutoreleasePool是被嵌套的!
池是被嵌套的,嵌套的結(jié)果是個(gè)棧,同一線程只有當(dāng)前棧頂pool實(shí)例是可用的:
| pool_3 |
| --------- |
| pool_2 |
| --------- |
| pool_1 |
|_______|
其代碼如下:
NSAutoreleasePool *pool1 = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool *pool3 = [[NSAutoreleasePool alloc] init];
NSObject *o = [[NSObject alloc] init] autorelease];
[pool3 release];
[pool2 release];
[pool1 release];
我們可以看到其棧頂是pool3,o的autorelease是把當(dāng)前的release放在棧頂?shù)膒ool實(shí)例管理。。。也就是pool3。
在生命周期短,產(chǎn)生大量放在autoreleasePool中管理實(shí)例的情況下經(jīng)常用此方法減少內(nèi)存使用,達(dá)到內(nèi)存及時(shí)回收的目的。
AutoreleasePool還被用在哪里?
在上面的例子里,也可以看到,我們?cè)趫?zhí)行autorelease方法時(shí),并沒有時(shí)時(shí)的進(jìn)行release操作〜它的release被延時(shí)到pool實(shí)例的dealloc方法里。這個(gè)小細(xì)節(jié)使我們的Objective-C用起來可以在方法棧中申請(qǐng)堆中的內(nèi)存,創(chuàng)建實(shí)例,并把它放在當(dāng)前pool中延遲到此方法的調(diào)用者釋放〜
以上就是我想到的內(nèi)存管理總結(jié)〜〜〜〜也就這么多吧〜日常工作用夠用了〜不夠的,沒想到的大家補(bǔ)充〜