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

解决的问题


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


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


你可能感兴趣的:(Objective-C)