AutoRelease 浅析

本篇将给您介绍”Autorelease Pools”(自动释放池)在应用中的使用技巧。

1,Autorelease Pools概要

一个”Autorelease Pool”实例中“包含”其它各种调用了”autorelease”方法的对象。当它释放时,其中所有被管理对象都会收到”relrease”的消信。注意,同一个对象可以被多次调用”autorelease”方法,并可以放到同一个”Autorelease Pool”中。引入这个自动释放池机制,对象的”autorelease”方法代替”relrease”方法可以延长它的生命周期,直接到当前”Autorelrease Pool”释放。如果想让此对象的生命周期超过”Autorelease Pool”,还可以再次”retain”,呵呵,有意思吧〜且让我慢慢道来。

Cocoa总是认为当前至少有一个”Autorelease Pool”对象是可用的。若此对象并不存在,你调用的”autorelease”的所有对象都不会被自动释放掉,可想而知,造成内存泄露。Cocoa把这个错误信息写入日志〜〜仅仅是为了以后分析。

你可以用”alloc”与”init”方法创建一个”NSAutoreleasePool”对象,并且可以调用”release”或”drain”(”release”与”drain”的区别是”drain”在有GC的环境中会引起GC回收操作,”release”反之。但在非GC环境中,两者相同。官方的说法是为了程序的兼容性,应该考虑用”drain”代替”release”,)方法来回收它(调用它的”autorelease”或”retain”方法会引起异常)。在一个完整的上下文最后”Autorelease Pool”对象应该被”release”掉(在方法内或一段循环体内创建的”Autorelease Pool”对象)。

“Autorelease Pools”的所有实例在栈中管理(我们暂时叫他“自动释放池栈”),并且它们是可以被嵌套的(父生子,子生孙。。。子子孙孙 ^_^)。例如,当我们创建一个”Autorelease Pool”对象后,它就被自动放到“自动释放池栈”的栈顶。当本池对象回收时,它就随之从这个栈中POP掉。那么也就是说,当任何一个对象调用”autorelease”方法后,它会被放入当前线程中当前栈顶的自动释放池中。

接下来我们聊聊”Autorelease Pools”的嵌套问题。在你的应用中,你可以任意多的创建”Autorelease Pool”对象,而这些对象被当前线程的“自动释放池栈”所管理。那么除了一个接一个的顺序创建并销毁它的情况外,还有一种使用方式,就是嵌套式的创建与使用。例如:在你的主函数创建了一个”autorelease pool”,然后又调用了创建了”autorelease pool”实例的其它方法;或是在外循环中创建了”Autorelease Pool”的实例,而内循环中也做了相同的事情。有意思吧,呵呵,嵌套的机制使父Pool实例释放后,它的所有子Pool也将释放。但这里还存在一些副作用,后续文章会详细讨论。

“Application kit”在一个事件循环里会自动创建一个”autorelease pool”。像鼠标键的按下与释放,所以你编写的代码通常不需要考虑太多这方面的事情。当然,有以下三种情况你会创建与销毁自己的Pool实例:

    1,应用不是基于”Application Kit”,像”Command-line tool”,因为它并没有内置的”autorelease pools”的支持。
    2,创建线程,你必需在线程开始时创建一个”Autorelease Pool”实例。反之,会造成内存池露(会在以后的文章详细说明线程与池的技巧)。
    3,一个循环内创建了太多的临时对象,你应该为他们创建一个”Autorelease Pool”对象,并在下次循还前销毁它们。

2,自动释放池中的”Non-AppKit”应用

在”Non-AppKit”应用中使用自动释放池的机制其实是相当简单的事情。你仅仅需要在main()起始处创建”Autorelease Pool”对象,并在结尾处释放掉它。就像在Xcode的Foundation Tool的创建模版里写的一样。这个确保你在应用生命周期内至少有一个”Autorelease Pool”是可用的。但是,这也使所有在此期间的所有”autorelease”的对象都必需在应用结束后才被释放。这也许会引起在应用的使用中不断的增长,所以,你仍然考虑在不同的作用域创建新的”Autorelease Pool”。

大多应用中都存在各种级别的循环机制。在这些应用中,你可以在每个循环内的开头创建一个”Autorelease Pool”对象,并在结尾处释放掉它。

例如:


void main()
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
    NSArray *args = [[NSProcessInfo processInfo] arguments];
    unsigned count, limit = [args count];
 
    for (count = 0; count < limit; count++)
    {
        NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
        NSString *fileContents;
        NSString *fileName;
 
        fileName = [args objectAtIndex:count];
        fileContents = [[[NSString alloc] initWithContentsOfFile:fileName] autorelease];
        // this is equivalent to using stringWithContentsOfFile:
 
        /* Process the file, creating and autoreleasing more objects. */
 
        [loopPool release];
    }
 
    /* Do whatever cleanup is needed. */
    [pool drain];
 
    exit (EXIT_SUCCESS);
}

在命令行中处理所有以参数传来的文件。一次循环处理一个文件。在循环的开头创建一个”NSAutoreleasePool”对象,并在循环结束时释放掉。因此,任何在其中创建并调用“autorelease”的对象都将添加到这个Pool实例中,当本池被释放后,这些对象也将被回收。注意,任何在作用域内创建的”autoreleased”对象(像”fileName”),虽然并没有显示的调用”autorelease”方法,但都将被当前池所管理并释放。

二、

深入理解一下(autorelease)

 

1. retain之后count加一。alloc之后count就是1,release就会调用dealloc销毁这个对象。
如果 retain,需要release两次。通常在method中把参数赋给成员变量时需要retain。
例如:
ClassA有 setName这个方法:
-(void)setName:(ClassName *) inputName
{
   name = inputName;
   [name retain]; //此处retian,等同于[inputName retain],count等于2
}
调用时:
ClassName *myName = [[ClassName alloc] init];
[classA setName:myName]; //retain count == 2
[myName release]; //retain count==1,在ClassA的dealloc中release name才能真正释放内存。

2. autorelease 更加tricky,而且很容易被它的名字迷惑。我在这里要强调一下:autorelease不是garbage collection,完全不同于Java或者.Net中的GC。
autorelease和作用域没有任何关系!
autorelease 原理:
a.先建立一个autorelease pool
b.对象从这个autorelease pool里面生成。
c.对象生成 之后调用autorelease函数,这个函数的作用仅仅是在autorelease pool中做个标记,让pool记得将来release一下这个对象。
d.程序结束时,pool本身也需要rerlease, 此时pool会把每一个标记为autorelease的对象release一次。如果某个对象此时retain count大于1,这个对象还是没有被销毁。
上面这个例子应该这样写:
ClassName *myName = [[[ClassName alloc] init] autorelease];//标记为autorelease
[classA setName:myName]; //retain count == 2
[myName release]; //retain count==1,注意,在ClassA的dealloc中不能release name,否则release pool时会release这个retain count为0的对象,这是不对的。

    记住一点:如果这个对象是你alloc或者new出来 的,你就需要调用release。如果使用autorelease,那么仅在发生过retain的时候release一次(让retain count始终为1)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值