ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

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

2022-05-25 19:32:57  阅读:211  来源: 互联网

标签:函数 iOS 笔记 Person objc 内存 BOUNDARY page POOL


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有