解决的问题
每一个东西的产生都是在实际生活迫切需要这样的东西后,人们通过思考发明创造某些东西用以解决这个问题。那么自动释放池用来解决什么问题?请看下面的例子:
对于一些有返回值的消息(方法),它返回的对象要交给谁来释放其内存,比如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 // RetainTracker
int 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 pool
2015-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 pool
2015-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 pool
2015-02-01 00:18:23.934 09.02 RetainCount-2[876:61410] dealloc called. Bye Bye.
Program ended with exit code: 0