系统相关
首页 > 系统相关> > iOS笔记 - 内存管理:自动释放池的底层实现

iOS笔记 - 内存管理:自动释放池的底层实现

作者:互联网

Autorelease底层实现

1 - 在了解 Autorelease之前,我们首先要了解自动释放池 __AtAutoreleasePool

① 在 main函数中创建 MJPerson对象

② 我们转换成 C++代码,如下

我们就看到了 __AtAutoreleasePool。把 C++代码按照 OC格式书写

原来 __AtAutoreleasePool是一个结构体。注:C++中的结构体类似 OC中的类

当代码执行到第 36行声明局部变量 __autoreleasepool时,就会自动调用结构体 __AtAutoreleasePool里面的 objc_autoreleasePoolPush()函数

当代码执行到第 38 行作用域 } 的结束,则自动调用 __AtAutoreleasePool里面的 objc_autoreleasePoolPop()函数

③ 到这里我们就知道了入池、出池的动作实际上是分别调用了 objc_autoreleasePoolPush和 objc_autoreleasePoolPop函数

2 - objc_autoreleasePoolPush | objc_autoreleasePoolPop

① push()函数。如果没有 pool page,就新建一个并把 POOL_BOUNDARY添加到 pool page中;如果存在 pool page则调用 autoreleaseFast函数(该函数会判断是否翻页),同样把 POOL_BOUNDARY添加到 pool page中

我们可以顺便扫两眼 autoreleaseFast函数

② pop函数,token就是 POOL_BOUNDARY

通过以上代码我们知道,pop函数其实就是把 token传递给 stop,然后调用 releaseUtil(stop)函数遍历检测,直到释放对象

③ 我们回过头来看一看 OC中的 Aurorelease方法:首先走的是 rootAutorelease、其次 rootAutorelease2、再次 aurorelease,最终还是调用了 

autoreleaseFast函数

3 - AutoreleasePoolPage:上面说了那么多,关键点还是看 AutoreleasePoolPage

① 打开 AutoreleasePoolPage,这里只保留了用到的七个成员变量

② 如下有一个 pool page

begin() 其实就是起始地址 0x1000 + 这个 page的大小。每个 AutoreleasePoolPage对象占 4096字节,用来存储它的成员变量,剩下的存放 autorelease对象地址

如果存放的 autorelease对象超出 4096个字节系统就会启用一个新的 AutoreleasePoolPage直至把对象全部存进,所有 AutoreleasePoolPage对象是通过双向链表形式链接在一起的,这就是成员变量 parent/child的作用

注:id *next指向的是下池中的下一个对象,而不是下一个 pool page

我们可以大致看一下 begin/end相关函数

结语

1 - 自动释放池的底层工作原理

① 首先调用 push函数会将一个 POOL_BOUNDARY(默认 nil)入栈,并返回其内存地址 0x1038,注:是从 begin处开始,而不是从 0x1000处开始

② 最后调用 pop函数时会传入 POOL_BOUNDARY的内存地址,然后从最后一个入栈的对象开始发送 release消息,直到遇到这个 POOL_BOUNDARY

注:其实关键点就是对 AutoreleasePoolPage的使用

2 - 可以调用私有函数 void _objc_autoreleasePoolPrint(void)查看自动释放池的信息

 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(你可以在自动释放池中遍历出几百个对象验证)

 

标签:函数,iOS,笔记,Person,objc,内存,BOUNDARY,page,POOL
来源: https://www.cnblogs.com/self-epoch/p/16310484.html