OC进阶 - 自动释放池实现原理 <objc4-723>
▶ AutoreleasePool
本节开始,我们一步一步来窥视自动释放实现原理!首先在 main函数 中创建实例对象
将 main.m 转换成 C++代码
C++ 对应的 OC代码如下
同样地在 C++文件 中找到结构体 __AtAutoreleasePool
当代码执行到第 5 行声明局部变量 __autoreleasepool 时就会自动调用 __AtAutoreleasePool 里面的 objc_autoreleasePoolPush函数
当代码执行到第 7 行作用域 } 的结束,则自动调用 __AtAutoreleasePool 里面的 objc_autoreleasePoolPop函数
就是说自动释放池的入池、出池两个动作实际上是分别调用了 objc_autoreleasePoolPush、objc_autoreleasePoolPop 函数。下面新增了两个自动释放池
▶ objc_autoreleasePoolPush | objc_autoreleasePoolPop
打开 runtime源码我们很容易发现 objc_autoreleasePoolPush 和 objc_autoreleasePoolPop 分别调用了 push函数 和 pop函数
push函数
A. 刚开始如果没有 pool page,那么就新建一个 pool page 并把 POOL_BOUNDARY 添加到 pool page 中
B. 如果存在 pool page 则调用 autoreleaseFast函数(该函数会判断是否翻页),同样把 POOL_BOUNDARY 添加到 pool page 中
这里我们可以简单了解下 autoreleaseFast函数
pop函数:token 就是 POOL_BOUNDARY
pop函数 其实就是把 token 传递给 stop,然后调用 releaseUtil(stop)函数 遍历检测,直到释放对象
现在我们回过头看一看 Aurorelease方法 的实现步骤
A. 先是 rootAutorelease
B. 其次 rootAutorelease2
C. 再次 aurorelease
D. 最终还是调用了 autoreleaseFast函数
▶ AutoreleasePoolPage函数
AutoreleasePoolPage 是自动释放池的关键点。在 runtime源码 中找到 AutoreleasePoolPage函数,我们这里只保留了用到的七个成员变量,如下
比如存在一个 pool page
begin() 其实就是起始地址 0x1000 + 这个 page 的大小
每个 AutoreleasePoolPage 对象占 4096 字节:起始部分用来存储它的成员变量,剩下的存放 autorelease 所管理的对象地址。如果存放的对象超出 4096 个字节系统就会启用一个新的 AutoreleasePoolPage 直至把对象全部存进。所有 AutoreleasePoolPage 对象是通过双向链表形式链接在一起的,这就是成员变量 parent/child 的作用
id *next指向的是下池中的下一个对象,而不是下一个 pool page
这里可以大致了解下 begin/end 函数
▶ 结语
自动释放池的底层工作原理:其实关键点就是 AutoreleasePoolPage 工作原理
A. 首先调用 push函数 会将一个 POOL_BOUNDARY(默认 nil)入栈,并返回其内存地址 0x1038,注:是从 begin 处开始,而不是从 0x1000 处开始
B. 最后调用 pop函数 时会传入 POOL_BOUNDARY 的内存地址,然后从最后一个入栈的对象开始发送 release 消息,直到遇到这个 POOL_BOUNDARY
通过调用 私有函数void _objc_autoreleasePoolPrint() 可查看自动释放池的信息
1 #import <Foundation/Foundation.h> 2 #import "Person.h" 3 // 系统私有函数,引用后 Runtime会自动查询调用 4 extern void _objc_autoreleasePoolPrint(void); 5 int main(int argc, const char * argv[]) { 6 7 @autoreleasepool { // POOL_BOUNDARY 8 Person *p1 = [[[Person alloc] init] autorelease]; // p1 9 10 @autoreleasepool { // 又进来一个 POOL_BOUNDARY 11 Person *p2 = [[[Person alloc] init] autorelease]; // p2 12 13 @autoreleasepool { // 又进来一个 POOL_BOUNDARY 14 Person *p3 = [[[Person alloc] init] autorelease]; // p3 15 Person *p4 = [[[Person alloc] init] autorelease]; // p4 16 17 // 应该是: 7 releases pending. 18 _objc_autoreleasePoolPrint(); 19 // page 中的有 3 个 POOL_BOUNDARY+ 4 个 Person 对象 20 NSLog(@"-------------------------\n"); 21 } 22 23 // 4 releases pending. 24 // 最近的 @autoreleasepool 已释放 25 _objc_autoreleasePoolPrint(); 26 NSLog(@"-------------------------\n"); 27 28 } 29 30 // 2 releases pending. 31 _objc_autoreleasePoolPrint(); 32 } 33 34 return 0; 35 36 }
日志信息:hot 是指当前页,因为对象过多则会有多个 pool page!你完全可以在自动释放池中遍历出数百个实例对象进行验证
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)