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!你完全可以在自动释放池中遍历出数百个实例对象进行验证

 

posted on   低头捡石頭  阅读(69)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示