[iOS底层]-autoreleasepool底层原理

autoreleasepool的基本原理

下载Demo,通过使用Clang编译器把OC文件编译成C++文件的学习,我们已经知道如何把OC编译成C++。接下来就把main.m编译成main.cpp,得到@autoreleasepool的C++的底层源码:

int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;

appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class")));
}
return UIApplicationMain(argc, argv, __null, appDelegateClassName);
}

可以很直观的看出,autoreleasepool被解析成一个代码块{ },并且创建了一个__AtAutoreleasePool __autoreleasepool;结构体。

struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};

autoreleasepool原理如下:

  1. 放在autoreleasepool中的变量和方法都是在一个局部作用域中执行。
  2. 这个局部作用域中第一行会先创建一个__AtAutoreleasePool结构体对象,这个时候会调用结构体初始化方法__AtAutoreleasePool(),进行objc_autoreleasePoolPush
  3. 等作用域所有代码执行完毕,作用域进行销毁,此时__AtAutoreleasePool结构体对象也会销毁,调用析构方法~__AtAutoreleasePool(),执行objc_autoreleasePoolPop

objc_autoreleasePoolPush方法分析

// objc_autoreleasePoolPush调用了AutoreleasePoolPage的push方法
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}

// 注意苹果这段注释,对autoreleasepool的实现做了一个说明
/***********************************************************************
Autorelease pool implementation

A thread's autorelease pool is a stack of pointers.
Each pointer is either an object to release, or POOL_BOUNDARY which is
an autorelease pool boundary.
A pool token is a pointer to the POOL_BOUNDARY for that pool. When
the pool is popped, every object hotter than the sentinel is released.
The stack is divided into a doubly-linked list of pages. Pages are added
and deleted as necessary.
Thread-local storage points to the hot page, where newly autoreleased
objects are stored.
**********************************************************************/
class AutoreleasePoolPage : private AutoreleasePoolPageData

objc_autoreleasePoolPush调用的是AutoreleasePoolPage类的push方法,找到AutoreleasePoolPage定义的地方,可以看到注释部分,苹果对Autorelease pool的实现做了一个说明:

  1. autoreleasepool是一个栈结构的指针集,其中存储是要释放的对象,或者是一个表示边界的哨兵对象POOL_BOUNDARY
  2. pool进行pop操作的时候,遇到了POOL_BOUNDARY,表示对象都释放完了。
  3. 栈存储在page中,pagepage之间通过双向链表进行连接。
  4. 当前操作的页会标识为hot page,并且线程会进行存储。

AutoreleasePoolPage结构

struct AutoreleasePoolPageData
{
magic_t const magic;
__unsafe_unretained id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;

AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
: magic(), next(_next), thread(_thread),
parent(_parent), child(nil),
depth(_depth), hiwat(_hiwat)
{
}
};

AutoreleasePoolPage继承自AutoreleasePoolPageDataAutoreleasePoolPageData的定义如上所示:

  1. magic用来校验AutoreleasePoolPage的结构是否完整
  2. next指向新添加的autoreleased对象的下一个位置,初始化时指向begin();
  3. thread指向当前线程
  4. parent指向父节点,第一个结点的parent值为nil
  5. child指向子结点,最后一个结点的child为nil
  6. depth代表深度,从0开始,往后递增1
  7. hiwat代表最大入栈数量标记

objc_autoreleasePoolPush流程

根据push方法的源码,可以得出下列调用流程图

可以得出下面的结构图

  1. page和page之间是双向链表链接,首页的parent = nil,尾页的child = nil
  2. 每个page内部都是栈结构,首页栈底是POOL_BOUNDARY,然后往栈里push销毁对象。
  3. 当前page设置为hotPage,next表示hotPage的栈顶,用来标识下次push的位置。

objc_autoreleasePoolPop方法分析

  1. 先找到当前边界stop标志
  2. 循环销毁对象
    1. 通过hotPage找到当前page,判断page是否为空,为空表示当前页的待销毁对象已经销毁完毕,继续找parent page的待销毁对象。
    2. 通过next找到待销毁对象,然后进行销毁。
    3. 遇到stop标识表示所有对象都已经销毁。