iOS 中,autoreleasepool 的底层实现

在 iOS 中,autoreleasepool 的底层实现基于 Objective-C 运行时(runtime)和内存管理机制。

3 个核心概念:AutoreleasePoolStack,AutoreleasePool,AutoreleasePoolPage

AutoreleasePoolStack 的概念

线程独立性:

每个线程都有一个独立的 AutoreleasePoolStack。这意味着每个线程都能独立管理其自动释放池,而不会干扰其他线程的内存管理。

结构:

AutoreleasePoolStack 是一个栈结构,用于存储当前线程中的所有 AutoreleasePool。

工作流程:

1. 创建 AutoreleasePool:

当进入 @autoreleasepool 块时,会创建一个新的 AutoreleasePool 并压入到 AutoreleasePoolStack 栈中。

@autoreleasepool {
    // 创建新的 AutoreleasePool 并压入栈中
}
----------------------
| AutoreleasePoolStack |
|----------------------|
|     AutoreleasePool  |  <-- 新创建的 pool
|----------------------|
|     AutoreleasePool  |  <-- 之前的 pool
----------------------
2. 销毁 AutoreleasePool:

当 @autoreleasepool 块结束时,当前 AutoreleasePool 会被弹出栈,并遍历其 AutoreleasePoolPage,释放所有对象。

理解 AutoreleasePoolPage

是个什么 ?

AutoreleasePool 实际上是由多个 AutoreleasePoolPage 组成,每个页面大小固定,用于存储对象指针,使用链表 (双向) 链接。每当一个页面填满时,会创建一个新的页面。

---------------------
| AutoreleasePoolPage|
|--------------------|
|       obj1         |
|       obj2         |
|       ...          |
|--------------------| --> Next Page
|       objN         |
|--------------------|

AutoreleasePoolPage 负责管理那些被标记为自动释放的对象。

被标记为自动释放的对象:MRC 模式下,对象需要手动调用 autorelease,而 ARC 模式下,ARC 会根据上下文自动插入 autorelease 调用,而不需要开发者显式地调用它。

结构是什么样的 ?

每个 AutoreleasePoolPage 是一个双向链表节点,链表头指向当前活跃的页面,尾部是最早创建的页面。新的 AutoreleasePoolPage 创建时会插入到链表头部。

每个 AutoreleasePoolPage 节点包含以下重要部分:

  1. 指针数组:存储需要自动释放的对象指针。
  2. 页的大小:一个页面可以容纳的对象数量是固定的。
  3. 下一个页面:指向链表中的下一个页面。
  4. 上一个页面:指向链表中的上一个页面。

清理过程

AutoreleasePool 清理时,它会遍历(从后向前)这些页面并调用每个对象的 release 方法。

多个 AutoreleasePoolPage 是按照从最后一个创建的页面到第一个页面的顺序依次销毁的。

  1. 遍历对象
    AutoreleasePool 块结束时,系统会遍历当前 AutoreleasePool 中所有的 AutoreleasePoolPage

    AutoreleasePoolPage *page = currentAutoreleasePoolPage;
    while (page) {
        // 遍历当前页面中的对象
        for (id *ptr = page->begin(); ptr < page->end(); ptr++) {
            // 调用对象的 release 方法
            (*ptr)->release();
        }
        // 移动到上一个页面
        page = page->previous;
    }
    
  2. 调用 release
    每个页面中的对象指针都会接收到 release 消息,从而减少其引用计数。如果某个对象的引用计数变为零,则会被立即销毁

  3. 页面的销毁
    一旦页面中的所有对象都被处理完毕,该页面会被销毁,释放其占用的内存。

示例代码

以下是一个简化版的 AutoreleasePoolPage 清理过程的代码示例:

@interface AutoreleasePoolPage : NSObject {
    id *next; // 指向下一个空闲的位置
    AutoreleasePoolPage *previous;
    AutoreleasePoolPage *nextPage;
    id objects[POOL_SIZE]; // 存储对象指针的数组
}
- (void)releaseAllObjects;
@end

@implementation AutoreleasePoolPage
- (void)releaseAllObjects {
    for (id *ptr = objects; ptr < next; ptr++) {
        [*ptr release];
    }
    if (previous) {
        [previous releaseAllObjects];
    }
}
@end

在这个示例中,releaseAllObjects 方法遍历当前页面中的所有对象指针,并调用它们的 release 方法。遍历完成后,如果有前一个页面,则递归调用其 releaseAllObjects 方法。

通过这种机制,AutoreleasePoolPage 能够高效地清理所有需要自动释放的对象,从而实现内存管理。


  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

依旧风轻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值