深度解析Objective-C內(nèi)存管理教程
深度解析Objective-C內(nèi)存管理教程是本文要介紹的內(nèi)容,不多說,來看內(nèi)容。iPhone系統(tǒng)中的Objective-C的內(nèi)存管理機(jī)制是比較靈活的,即可以拿來像C/C++一樣用,也可以加個(gè)AutoreleasePool讓它升級(jí)為半自動(dòng)化的內(nèi)存管理語(yǔ)言。當(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方法。
主要操作接口:
1、alloc, allocWithZone,new(帶初始化)
為對(duì)象分配內(nèi)存,retainCount為“1”,并返回此實(shí)例
2、retain
- retainCount 加“1”
3、release
retainCount 減“1”,減到“0”時(shí)調(diào)用此對(duì)象的dealloc方法
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)化。
- - (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)單,你不需要考慮過多的事情。
數(shù)組(Array)是一個(gè)比較特別的例子,當(dāng)你往數(shù)組里面添加一個(gè)對(duì)象時(shí)。數(shù)組里面存儲(chǔ)的并不是這個(gè)對(duì)象的拷貝,而只是一個(gè)指向該對(duì)象的指針。數(shù)組在保存這個(gè)指針的同時(shí)會(huì)向指針?biāo)傅膶?duì)象發(fā)送一個(gè)retain消息,相應(yīng)的,對(duì)象的持有計(jì)數(shù)會(huì)增加。將對(duì)象從數(shù)組中移除的時(shí)候,同樣會(huì)向?qū)ο蟀l(fā)送release消息,對(duì)象的持有計(jì)數(shù)會(huì)減小。當(dāng)我們釋放這個(gè)數(shù)組時(shí),會(huì)向保存在這個(gè)數(shù)組中的所有對(duì)象發(fā)送release消息。看下面的兩個(gè)例子:
1、沒有釋放內(nèi)存的版本
- array = [[NSMutableArray alloc] init];
- for ( i = 0; i < 10; i++) {
- newNumber = [[NSNumber alloc]initWithInt:(i * 3)];
- [array addObject:newNumber];
- }
上面的代碼在創(chuàng)建newNumber對(duì)象時(shí),向?qū)ο蟮陌l(fā)送了retain消息,對(duì)象的持有計(jì)數(shù)變?yōu)?。當(dāng)向array中添加這個(gè)對(duì)象的引用時(shí),又向?qū)ο蟀l(fā)送了一次retain消息,這樣對(duì)象的持有計(jì)數(shù)就變?yōu)?了,在使用完array時(shí),我們會(huì)習(xí)慣性的釋放掉array,但這樣并不會(huì)釋放array所持有的對(duì)象,而只是使所有對(duì)象的持有計(jì)數(shù)變?yōu)?,這些對(duì)象依然會(huì)占用著內(nèi)存。
2、釋放內(nèi)存的版本
- for (i = 0; i < 10; i++) {
- newNumber = [[NSNumber alloc]initWithInt:(i*3)];
- [array addObject:newNumber];
- [newNumber release];
- }
AutoreleasePool使 Objective-C成為內(nèi)存管理半自動(dòng)化語(yǔ)言。
如果僅僅是上面這些,很簡(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)用者釋放.
小結(jié):深度解析Objective-C內(nèi)存管理教程的內(nèi)容介紹完了,希望本文對(duì)你有所幫助!