Autorelease Pool
前言
之前有写过一篇关于AutoreleasePool的理解的总结,总觉的不够深入,所以重起一篇。本篇从阅读官方文档和autorelasepool的源码入手来记录。
Autorelease Pool
什么是autorelease pool
从官方文档中的解释,autorelease pool存储着一些对象,当向pool发送drain消息时会向池中全部对象发送release消息。
autorelease pool的创建和池内对象的释放时机
应用程序在每次事件循环(event loop)的开始,自动在主线程上创建一个自动释放池,并在每次事件循环(event loop)结束时向池内对象发送release消息进行释放。手动创建的autorelease pool中的对象在block外被释放掉。
autorelease pool 与 thread
当你通过NSThread另外开辟的子线程时,你需要自己创建一个autorelease pool。子线程默认是没有自动释放池的。但是使用GCD开辟的线程会自动创建pool。
autorelease pool的结构
我们都知道在iOS开发中,main函数作为程序的入口,包含了一个@autoreleasepool,我们通过clang重新编译一下会发现main函数的实现变成了下图的样子
@autoreleasepool被转成了一个__AtAutoreleasePool。__AtAutoreleasePool是一个结构体
结构体的构造函数中会调用objc_autoreleasePoolPush(),析构函数中会调用objc_autoreleasePoolPop(void *)
这俩个方法可以再NSObject.mm的源码中找到实现。
方法实现中出现了一个新的类AutoreleasePoolPage,提取出来的大致定义是这样的
magic 用来校验 AutoreleasePoolPage 的结构是否完整;
next 指向最新添加的 autoreleased 对象的下一个位置,初始化时指向 begin() ;
thread 指向当前线程;
parent 指向父结点,第一个结点的 parent 值为 nil ;
child 指向子结点,最后一个结点的 child 值为 nil ;
depth 代表深度,从 0 开始,往后递增 1;
hiwat 代表 high water mark
当next指向begin时代表该page为空,当next指向end时代表page已满。
autorelease pool就是由这些page组成的双向链表结构且每一个page的可用空间是固定的4096个字节
objc_autoreleasePoolPush
上面说了_AtAutoreleasePool在初始化的时候会调用objc_autoreleasePoolPush,我们来看下这个方法的实现,整理后差不多是这样
调用autoreleaseFast并传入POOL_BOUNDARY。
POOL_BOUNDARY(哨兵)
POOL_BOUNDARY实际上就是nil,当调用objc_autoreleasePoolPush方法的时候,POOL_BOUNDARY被最先加入到pool中并置于栈顶。同时哨兵的地址也作为objc_autoreleasePoolPop方法的入参来寻找需要进行pop操作的page。
autoreleaseFast (hotPage:当前正在使用的page)
大体的逻辑是这样
- 通过hotPage()方法获取hotPage
- 当hotPage存在并且空间未满时将传入的obj添加到page中
- 如果hotPage满了,就另外新建一个page并将obj添加到里面
- 如果hotPage不存在,就创建一个hotPage并将obj添加到里面
hotPage
通过tls(Thread Local Storage)获取hotPage
page->add()
autoreleaseFullPage
autoreleaseNoPage
创建一个parent为nil的page并添加一个哨兵,之后再将传入的obj添加到page中。
那么一个obj什么时候被添加到autorelease pool中的呢。只有当一个对象被调用了autorelease方法的时候,该对象才会被加入到autorelease pool中。在MRC的环境下,开发者需要进行手动调用,而ARC的环境下,非alloc/copy/mutablecopy创建的对象都是autorelease对象,若想将通过alloc/copy/mutablecopy创建的对象变为autorelease对象只需要创建时在前面添加__autorelease关键字。
autorelease
rootAutorelease
rootAutorelease2
确保非TaggedPointer后调用page的autorelease方法,之后进入autoreleaseFast流程成功将对象加入到autorelease pool中。
objc_autoreleasePoolPop操作
大体上的执行顺序包括pop() ==> releaseUntil(stop) ==> page->child->kill()
也就是根据传入的stop不断释放对象直到stop的地址,之后开始kill掉childPage。
而objc_autoreleasePoolPop调用时机就是当[NSAutoreleasePool release]的时候。