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 <UIKit/UIKit.h>
#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;
};
![](https://img.haomeiwen.com/i18883237/be69219ed2a39cfd.png)
每个自动释放池都是由一系列的AutoreleasePoolPage组成的,并且每个AutoreleasePoolPage的大小都是4096字节(也就是虚拟内存一页的大小)。
![](https://img.haomeiwen.com/i18883237/97fefd816c9129de.png)
由此可见Autoreleasepool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成(分别对应结构中的parent指针和child指针)。
三、释放原理
我们将自动释放池的代码编译成C++代码后发现:
@autoreleasepool {
// code
}
对应的C++代码:
void *context = objc_autoreleasePoolPush();
// {}中的代码
objc_autoreleasePoolPop(context);
当进入自动释放池的时候调用objc_autoreleasePoolPush(),根据传入的哨兵对象地址找到哨兵对象所处的page:
![](https://img.haomeiwen.com/i18883237/76c223c698c06126.png)
当出自动释放池的时候调用objc_autoreleasePoolPop(context),在当前page中,将晚于哨兵对象插入的所有autorelease对象都发送一次release消息,并向回移动next指针到正确位置:
![](https://img.haomeiwen.com/i18883237/9acb650dbbd33750.png)
注意:清理自动释放池时,从最新加入的对象一直向前清理,可以向前跨越若干个page,直到哨兵所在的page。所以嵌套的AutoreleasePool就非常简单了,pop的时候总会释放到上次push的位置为止,多层的pool就是多个哨兵对象而已,就像剥洋葱一样,每次一层互不影响。
总结:
- 自动释放池是由AutoreleasePoolPage以双向链表的方式实现的
- 当对象调用autorelease方法时,会将对象加入AutoreleasePoolPage的栈中
- 调用AutoreleasePoolPage::pop方法会向栈中的对象发送release消息
- 每一个线程(包括主线程)都有一个NSAutoreleasePool(Page)栈
网友评论