解決的問題
每一個東西的產生都是在實際生活迫切需要這樣的東西後,人們通過思考發明創造某些東西用以解決這個問題。那麼自動釋放池用來解決什麼問題。請看下面的例子:
對於一些有傳回值的訊息(方法),它返回的對象要交給誰來釋放其記憶體,比如NSObject類中description訊息
-(NSString*)description{ NSString *description; description = [[NSString alloc]initWithFormat:@"hello world"]; return (description); }//description
這個時候description字串的記憶體釋放要交給誰。肯定不應該在description方法裡面,這樣的話,返回的對象指向的就是一個被釋放的記憶體了。這肯定是不可行的。
那麼如果讓調用者來釋放呢,那麼就得像下面這樣寫:
RetainTracker *tracker = [RetainTracker new]; NSString *desc = [tracker description]; [desc release];
那麼這樣寫有什麼問題麼。沒什麼問題,就是多了一行代碼。有什麼更好的方法,能唯寫一行代碼就能達到自動釋放的效果。既然有這個需求,那麼肯定會有人試著去解決這個問題。
解決方案
蘋果公司在Cocoa架構中有一個自動釋放池的概念,在NSObject類中提供了一個叫做autorelease的方法。
-(id)autorelease{};//autorelease
該方法預先設定了一條會在未來某個時間發送的release訊息, 返回的id代表接受這條訊息的對象。當我們調用該對象的autorelease方法的時候,實際上是把該對象放到自動釋放池,當自動釋放池被銷毀時,會向該池中的所有對象發送release訊息。這個時候我們把description 方法修改成如下形式:
-(NSString*)description{ NSString *description; description = [[NSString alloc]initWithFormat:@"hello world"]; return ([description autorelease]); }//description
這個時候我們在調用的地方只需要一句話就做了記憶體釋放的工作。不用在單獨添加調用release方法的語句。
建立
我們上面說了那麼多,都在說自動釋放池可以幫我們解決掉對象自動釋放的問題,但是這個自動釋放池也不是自己就存在的,它需要我們自己來顯示的建立。可以通過2種方式來建立自動釋放池。
通過@autoreleasepool關鍵字
當你使用@autoreleasepool{}時,所有在花括弧裡的代碼都會被放入這個池子中。但是任何在該花括弧的變數都無法在括弧外使用。
通過NSAutoreleasePool對象
該對象需要建立,在該對象建立語句和銷毀語句的代碼都相當於放入了自動釋放池中。
NSAutoreleasePool *pool; pool = [NSAutoreleasePool new]; RetainTracker *tracker = [RetainTracker new]; NSString *desc = [tracker description]; [desc release]; // count: 1 [tracker retain]; // count: 2 NSLog (@"%d", [tracker retainCount]); [tracker retain]; // count: 3 NSLog (@"%d", [tracker retainCount]); [tracker release]; // count: 2 NSLog (@"%d", [tracker retainCount]); [tracker release]; // count: 1 NSLog (@"%d", [tracker retainCount]); [tracker retain]; // count 2 NSLog (@"%d", [tracker retainCount]); [tracker release]; // count 1 NSLog (@"%d", [tracker retainCount]); [tracker release]; // count: 0, dealloc it [pool release];
上面的代碼就是一個自動釋放池建立過程。
推薦
優先使用@autoreleasepool{}關鍵字的方式來建立自動釋放池。
工作原理
那麼自動釋放池的工作原理到底是什麼。下面用一個例子來講解一下。
#import <Foundation/Foundation.h>@interface RetainTracker : NSObject@end // RetainTracker@implementation RetainTracker- (id) init{if (self = [super init]) {NSLog (@"init: Retain count of %d.", [self retainCount]);}return (self);} // init- (void) dealloc{NSLog (@"dealloc called. Bye Bye.");[super dealloc];} // dealloc@end // RetainTrackerint main (int argc, const char * argv[]){ NSAutoreleasePool *pool; pool = [[NSAutoreleasePool alloc] init]; RetainTracker *tracker; tracker = [RetainTracker new]; // count: 1 [tracker retain]; // count: 2 [tracker autorelease]; // count: still 2 [tracker release]; // count: 1 NSLog (@"releasing pool"); [pool release]; // gets nuked, sends release to tracker @autoreleasepool { RetainTracker *tracker2; tracker2 = [RetainTracker new]; // count: 1 [tracker2 retain]; // count: 2 [tracker2 autorelease]; // count: still 2 [tracker2 release]; // count: 1 NSLog (@"auto releasing pool"); } return (0);}
第一個代碼塊是使用NSAutoreleasePool對象建立的自動釋放池。和之前的語句相比,多了一個[tracker autorelease]語句,該語句做了啥。它並沒有給引用計數器的值做操作,而是把該對象添加到自動釋放池中,在自動釋放池中也有一個引用指向了該對象。當自動釋放池銷毀時,會向改對象發送一條release訊息。由於我們先向tracker對象發送了retain對象,計數器加1,然後發送release訊息,計數器減1,但是此時tracker對象的dealloc方法並不會調用,因為new方法讓計數器加1,所以pool對象的release方法沒調用之前,tracker對象的計數器的值為1,當pool對象的調用release後,自動釋放池的計數器為0,此時pool對象的dealloc方法會被調用,然後會想池中的對象發送release對象,那麼此時tracker對象的計數器值會變為0,tracker對象的dealloc方法被調用,所以輸出應該如下:
2015-02-01 00:12:02.426 09.02 RetainCount-2[860:58571] init: Retain count of 1.2015-02-01 00:12:02.428 09.02 RetainCount-2[860:58571] releasing pool2015-02-01 00:12:02.428 09.02 RetainCount-2[860:58571] dealloc called. Bye Bye.
另外一個@autoreleasepool關鍵字建立的自動釋放池也是一樣的過程。整個輸出如下:
2015-02-01 00:18:23.932 09.02 RetainCount-2[876:61410] init: Retain count of 1.2015-02-01 00:18:23.933 09.02 RetainCount-2[876:61410] releasing pool2015-02-01 00:18:23.934 09.02 RetainCount-2[876:61410] dealloc called. Bye Bye.2015-02-01 00:18:23.934 09.02 RetainCount-2[876:61410] init: Retain count of 1.2015-02-01 00:18:23.934 09.02 RetainCount-2[876:61410] auto releasing pool2015-02-01 00:18:23.934 09.02 RetainCount-2[876:61410] dealloc called. Bye Bye.Program ended with exit code: 0