自动释放池的数据结构

以栈为结点通过双向链表的形式组合而成且和线程一一对应的

AutoReleasePool工作流程

AutoReleasePool是OC的内存自动回收机制,将加入到AutoReleasePool中的变量release时机延迟。在正常情况下,创建的变量会在超出其作用域的时候release,但是如果将变量加入AutoreleasePool,那么release将延迟执行,即使超出作用域也不会立即释放,直到runloop休眠或者超出AutoReleasePool作用域才会释放。

程序启动到加载完成,主线程对应的Runloop处于休眠状态,直到用户点击交互唤醒Runloop,用户每次交互都会启动一次Runloop用来处理用户的点击、交互事件,Runloop被唤醒后,会自动创建AutoReleasePool,并将所有延迟释放的对象添加到AutoReleasePool,在一次完整的Runloop执行结束前,会自动向AutoReleasePool中的对象发送release消息,然后销毁AutoReleasePool。

自动释放池的本质.png

该结构体提供了一个构造函数objc_autoreleasePoolPush和一个析构函数objc_autoreleasePoolPop。所以自动释放池在底层其实是一个结构体,其通过objc_autoreleasePoolPush完成自动释放池的创建,objc_autoreleasePoolPop来释放自动释放池。

调用了autorelase的对象都是最终都是通过AutoreleasePoolPage来管理的,AutoreleasePoolPage的结构体如下:

625536cf2fb28.png

AutoreleasePoolPage中Push方法的内部实现

625538983c7a9.png

我们push操作,会把当前next的位置置为nil,也叫做哨兵对象,然后将next指针指向下一个可入栈的位置。

实际上每次进行AutoreleasePool的代码块创建的时候,相当于不断的在栈中去插入哨兵对象。

[obj autorelease]方法实现

当我们调用了一个对象的autorelease,首先会判断当前next指针是否指向栈顶,若没有指向栈顶,则直接把对象添加到当前栈的next位置。

假如当前next已经位于栈顶,那么当前AutoreleasePoolPage就没办法添加新的autorelease对象了,于是需要增加一个栈结点拼接到链表上,之后再新的栈上面添加对象。

AutoreleasePoolPage中Pop方法的内部实现

  1. 根据传入的哨兵对象来找到pop的对应位置。

  2. 给上次push操作之后添加的对象依次发送release消息。

autoreleasePool的多层嵌套调用

就是多次插入哨兵对象,当我们每次进行autoreleasePool代码块创建的时候,系统就会为我们进行哨兵对象的插入。

autoreleasePool的使用场景:

在for循环中alloc出大量的图片数据等内存消耗较大,需要在for循环内部手动插入autoreleasePool,每一次for循环,都进行一次内存的释放,来降低内存的峰值。