Objective-c自学笔记(5)-自动释放池

来源:互联网 发布:红叶知弦h同人 编辑:程序博客网 时间:2024/05/22 12:04

解决的问题


每一个东西的产生都是在实际生活迫切需要这样的东西后,人们通过思考发明创造某些东西用以解决这个问题。那么自动释放池用来解决什么问题?请看下面的例子:


对于一些有返回值的消息(方法),它返回的对象要交给谁来释放其内存,比如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


1 0