众所周知:一个AutoreleasePool对应一个RunLoop,一个RunLoop对应一个线程。但一个RunLoop可以包含多个AutoreleasePool。
本篇大致聊一聊AutoreleasePool:
本质:
AutoreleasePool的本质就是延迟 release 方法的调用。
MRC环境,可以通过调用 autorelease 来延迟内存的释放
ARC环境,甚至可以完全不知道 autorelease 也能管理好内存
ARC环境下:
- 以 alloc / init / new / copy / mutableCopy 开头的初始化方法:系统会在 调用方法的外围 加上内存管理代码 retain / release,所以其在作用域结束的时候就会被释放
- 以 其他 开头的初始化方法:系统会在 方法内部 自动加上 autorelease 方法,被注册到 AutoreleasePool 中,等到Pool dealloc时才释放
工作原理:
系统会在 RunLoop 每个运行循环之前(entry/beforeWaiting) 执行 autoreleasePoolPush 操作,会创建一个新的Page:在 当前Pool 的 next 位置插入一个Pool_Sentinel(哨兵对象),并返回其内存地址 poolToken,表示 新Pool 的起始位置。
- push哨兵对象 / autorelease 对象 :都会调用 autorelease Fas t(id obj) 来执行具体的 插入操作 :
当前Page存在且没满:直接添加至next指向位置;
当前Page存在且已满:创建一个新的Page,添加至新的page中;
当前Page不存在:创建第一个Page,添加至新page中。
在 RunLoop 结束之前,执行Pool的 autoreleasePoolPop 操作,传入poolToken,对哨兵对象之后添加的所有对象执行release。
- Pop(token) 沿着parent方向走到哨兵对象
- 使用 pageForPointer 获取当前 token 所在的 AutoreleasePoolPage
- 调用 releaseUntil 方法释放 栈中的 对象,直到 stop
- 调用 child 的 kill 方法(当前Page里的对象超过一半时,会保留child)
releaseUntil:(详情请看)
当next指针不为stop时,从当前page开始回溯, 当前page不为空时回移next指针,挨个对象release
内部结构:
class AutoreleasePoolPage { // 用C++实现的类
magic_t const magic; // 校验Page的完整性
id *next; // 指向将要添加新对象(Autorelease对象、哨兵对象)的空闲位置
pthread_t const thread; // 当前页所在的线程
AutoreleasePoolPage * const parent; // 双向链表中指向上一个节点,第一个结点的 parent 值为 nil
AutoreleasePoolPage *child; // 双向链表中指向下一个节点,最后一个结点的 child 值为 nil
uint32_t const depth; // 深度,从0开始,往后递增1
uint32_t hiwat; // high water mark
...
};
AutoreleasePool并没有单独的结构,而是由 若干个Page 以 双向链表 的形式组合而成的 指针堆栈
每个Page对象回开辟4096个字节内存(也就是虚拟内存一页的大小)
系统会根据保存对象地址数量动态的 增加 和 删除 page 节点
-每个Page除了Page自身的成员变量外,剩下的空间用 begin 和 end 用标识,存放 autorelease对象 和 哨兵对象 的内存地址
-当next指针作为游标指针:指向begin时,表示page为空;指向end时,标识page已满
-当一个page的空间被占满时,会新建一个page对象,连接链表,后来的autoRelease对象在新的page加入。
需要手动创建自动释放池:
- 编写不基于UI框架的程序,如命令行工具
- 编写一个创建许多临时对象的循环
- 生成辅助线程(必须在线程开始执行后立即创建Pool,否则将泄露对象。非Cocoa程序创建线程时才需要)
- 长时间在后台运行的任务。
enumerateObjectsUsingBlock内部有autoReleasePool
参考:
黑幕背后的Autorelease(后面的黑魔法看不懂>_<)
AutoreleasePool探索学习(转化为.cpp文件)
iOS探究 - autorelease 和 autoreleasepool(写得不错)
自动释放池的前世今生 ---- 深入解析 autoreleasepool (Page相关操作源码分析和结构示意图)