iOS开发之Autoreleasepool简介

Autoreleasepool即自动释放池,是在ARC自动管理内存机制下用来管理程序中开辟的内存的,ARC工程每个进程都有个全局自动释放池。MRC中,调用[obj autorelease]的对象都会放到Autoreleasepool中统一管理。

在没有手动添加AutoreleasePool的情况下,autorelease对象是在当前的runloop迭代结束时释放的。
一、用法
//MRC下用法
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Code benefitting from a local autorelease pool.
//    [pool release];
[pool drain];

NSAutoreleasePool有两种清理池内对象的方法:

  • drain和release都是清理自动释放池,向池内所有对象发送release消息;
  • drain在支持GC的系统(Mac系统)可以引起GC回收操作,而release不可以;
  • 一般使用drain,一是兼容强,二是和普通对象的release区分开。
//ARC和MRC下都适用,并且比上述方式高效
@autoreleasepool {
       // code
}
二、探索概念

在主线程中main.m里面一开始就创建了一个全局的Autoreleasepool:

#import 
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

在命令行里编译main.m文件后得到C++代码:

struct __AtAutoreleasePool {
        __AtAutoreleasePool() {
            atautoreleasepoolobj = objc_autoreleasePoolPush();
        }
        ~__AtAutoreleasePool() {
            objc_autoreleasePoolPop(atautoreleasepoolobj);
        }
        void * atautoreleasepoolobj;
    };
    
    void *objc_autoreleasePoolPush(void) {
        return AutoreleasePoolPage::push();
    }
    void objc_autoreleasePoolPop(void *ctxt) {
        AutoreleasePoolPage::pop(ctxt);
    }

我们可以看到Autoreleasepool的结构体只有构建和析构函数,并且进入Autoreleasepool会调用AutoreleasePoolPage::push(),出Autoreleasepool会调用AutoreleasePoolPage::pop(ctxt)。

再看AutoreleasePoolPage:

    class AutoreleasePoolPage  {
    magic_t const magic; //用于对当前 AutoreleasePoolPage 完整性的校验
    id *next;   //游标 指向栈顶最新add进来的autorelease对象的下一个位置
    pthread_t const thread;  //当前页所在的线程
    AutoreleasePoolPage * const parent;   //父节点链表指针
    AutoreleasePoolPage *child;   //子节点链表指针
    uint32_t const depth;  
    uint32_t hiwat;
     };

每个自动释放池都是由一系列的AutoreleasePoolPage组成的,并且每个AutoreleasePoolPage的大小都是4096字节(也就是虚拟内存一页的大小)。

由此可见Autoreleasepool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成(分别对应结构中的parent指针和child指针)。
三、释放原理

我们将自动释放池的代码编译成C++代码后发现:

@autoreleasepool {
        // code
    }

 对应的C++代码:

    void *context = objc_autoreleasePoolPush();
    // {}中的代码
    objc_autoreleasePoolPop(context);

当进入自动释放池的时候调用objc_autoreleasePoolPush(),根据传入的哨兵对象地址找到哨兵对象所处的page:

当出自动释放池的时候调用objc_autoreleasePoolPop(context),在当前page中,将晚于哨兵对象插入的所有autorelease对象都发送一次release消息,并向回移动next指针到正确位置:

注意:清理自动释放池时,从最新加入的对象一直向前清理,可以向前跨越若干个page,直到哨兵所在的page。所以嵌套的AutoreleasePool就非常简单了,pop的时候总会释放到上次push的位置为止,多层的pool就是多个哨兵对象而已,就像剥洋葱一样,每次一层互不影响。

总结:
  • 自动释放池是由AutoreleasePoolPage以双向链表的方式实现的
  • 当对象调用autorelease方法时,会将对象加入AutoreleasePoolPage的栈中
  • 调用AutoreleasePoolPage::pop方法会向栈中的对象发送release消息
  • 每一个线程(包括主线程)都有一个NSAutoreleasePool(Page)栈

参考:http://www.cocoachina.com/articles/10107

你可能感兴趣的:(iOS开发之Autoreleasepool简介)