当前位置: 首页 > news >正文

iOS 内存管理之 autoreleasePool

一、autoreleasePool是个啥

  • 一句话总结自动释放池本质就是把一筐对象延迟release;
  • MRC环境下,通过 [obj autorelease] 来延迟内存的释放;
  • ARC环境下,是不能手动调用,系统会自动给对象添加autorelease;

二、autoreleasePool原理

先简单总结下(ps:以下解释略显抽象):

  1. 首先有个筐用来记录要延迟释放的对象,这个筐的结构是由若干个以AutoreleasePoolPage对象为结点的双向链表组成;
  2. 其次得有个方法往这个筐里装对象,也还得有对应从这个筐里批量释放的对象的方法;那这两个方法可以解析源码发现push和pop方法;

这样,就可以实现延迟释放对象的能力;

三、源码赏析

ps:md写的时候,想从苹果开发开源项目平台找源码看,can’t be found了;

以main文件为例:

int main(int argc, char * argv[]) {@autoreleasepool {NSObject * rpObj = [[NSObject alloc] init];}
}
  1. 先转换成main.cpp文件查看底层调用方法:
在终端运行以下命令:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
  1. 查看main.cpp文件:
int main(int argc, char * argv[]) {/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; ///调用了objc_autoreleasePoolPushNSObject * rpObj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));}
}
  1. 有这么一个叫做__AtAutoreleasePool的结构体,这里可以看到装筐和出筐的push和pop方法;
struct __AtAutoreleasePool {
//构造函数__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
//析构函数~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}void * atautoreleasepoolobj;
};
  1. 简单看下push和pop方法

先看push方法,核心逻辑有三块:

  • 获取当前线程的 “热页”(hotPage):自动释放池由多个 AutoreleasePoolPage 组成栈结构(每个页大小为 4096 字节),“热页” 是当前正在使用的页(栈顶页)。
  • 推入 “池边界”(POOL_BOUNDARY):每个自动释放池对应一个特殊标记 POOL_BOUNDARY(本质是 nil),push() 方法会将该标记添加到热页中,作为当前池的 “起点”。后续添加的自动释放对象会依次存入页中,直到调用 objc_autoreleasePoolPop 时,释放从 “起点” 到当前位置的所有对象。
  • 无页时创建新页栈:若线程首次使用自动释放池(无页),autoreleaseNoPage() 会创建第一个 AutoreleasePoolPage,并初始化线程的页栈。
// 全局的自动释放池页栈(每个线程独立)
static pthread_key_t const key = AUTORELEASE_POOL_KEY;void *objc_autoreleasePoolPush(void) {// 获取当前线程的自动释放池页栈AutoreleasePoolPage *page = hotPage();if (page) {// 若存在当前页,直接在页中添加一个“池边界”(POOL_BOUNDARY)return page->push();} else {// 若不存在页,创建新的页栈,并添加池边界return autoreleaseNoPage();}
}

再看pop方法,核心逻辑有四块:

  • 获取热页:先拿到当前线程的 AutoreleasePoolPage 栈顶页(hotPage),确保操作的是当前正在使用的页;
  • 验证令牌:传入的 ctxt 必须是 push 时返回的 POOL_BOUNDARY(池边界标记),避免释放错误范围的对象(如传入无效指针会直接报错);
  • 反向释放对象:调用 releaseUntil(stop) 从栈顶反向遍历,直到遇到 stop(即 POOL_BOUNDARY):
    每次取出栈顶的自动释放对象,调用 objc_release 减少其引用计数(计数为 0 时对象销毁);
    若当前页释放为空,自动切换到前一页(栈底方向),继续释放,直到遍历到目标边界;
  • 清理空页:释放完成后,若当前页为空且不是栈中唯一页,将其从页栈中移除,优化内存占用。
// pop 函数:传入 push 返回的“池令牌”(POOL_BOUNDARY),释放池内对象
OBJC_EXPORT void objc_autoreleasePoolPop(void *ctxt) OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);void objc_autoreleasePoolPop(void *ctxt) {// 1. 获取当前线程的热页(正在使用的 AutoreleasePoolPage 栈顶页)AutoreleasePoolPage *page = hotPage();if (!page) {// 无有效页,直接返回(异常场景)return;}// 2. 验证传入的“池令牌”是 POOL_BOUNDARY(push 时存入的边界标记)if (ctxt != POOL_BOUNDARY) {// 若令牌无效(如传入错误指针),释放到栈底并报错page->releaseUntil(POOL_BOUNDARY);_objc_fatal("invalid autorelease pool token");}// 3. 释放从“当前池边界”到“栈顶”的所有自动释放对象page->releaseUntil(ctxt);// 4. 清理空页:若当前页释放后无对象,且不是唯一页,将其从栈中移除if (page->empty() && !page->isSinglePage()) {page->kill();}
}// 核心辅助方法:释放从当前位置到目标边界(ctxt)的所有对象
void AutoreleasePoolPage::releaseUntil(void *stop) {// 从栈顶开始,反向遍历对象while (this->next != stop) {// 获取当前栈顶对象AutoreleasePoolPage *page = hotPage();while (page->empty()) {// 若当前页空,切换到前一页(栈底方向)page = page->parent;setHotPage(page);}// 取出栈顶对象,指针下移id obj = *--page->next;memset((void*)page->next, SCRIBBLE, sizeof(*page->next)); // 擦除痕迹,避免野指针// 释放对象(调用 objc_release,触发引用计数-1)objc_release(obj);}// 更新热页(确保后续操作指向正确页)setHotPage(this);
}
  1. 从上面看源码引出了AutoreleasePoolPage的概念,也就是这个承载延迟释放对象的筐;
class AutoreleasePoolPage {// 1. 常量定义static const size_t SIZE = 4096; // 每页大小(4KB,固定)static const size_t COUNT = SIZE / sizeof(id); // 每页可存储的对象数(约 1024 个,因头部占部分空间)static const id POOL_BOUNDARY = nil; // 池边界标记(push/pop 的核心标记)// 2. 成员变量(页的元数据 + 存储区)magic_t magic; // 校验页完整性的魔术值(调试用)id *next; // 指向栈顶的下一个空闲位置(即下一个对象的存储地址)pthread_t thread; // 所属线程(线程隔离,每个线程的页栈独立)AutoreleasePoolPage *parent; // 前一页(栈底方向)AutoreleasePoolPage *child; // 后一页(栈顶方向)uint32_t depth; // 页在栈中的深度(用于调试)uint32_t hiwat; // 历史最高存储量(用于调试)// 3. 存储区(紧跟在成员变量后,占 SIZE - 元数据大小的空间)id objects[0]; // 柔性数组,实际存储自动释放对象的区域
};

综上,有筐,有进筐方法,出筐方法;

http://www.dtcms.com/a/601883.html

相关文章:

  • 北京沙河教做网站的山东省济南市莱芜区
  • 长沙建长沙建网站公司给wordpress程序提速
  • 多国语言编译库 | 适用于全球化开发的高效工具
  • C语言编译系统 | 如何高效构建和使用C语言编译系统
  • 手机建设银行网站首页网站开发app开发主营业务
  • 11月12日星期三今日早报简报微语报早读
  • 360水滴摄像头重新设置摄像头wifi
  • Notepad++ 编译 C 语言的使用方法与技巧
  • 编译C语言的软件 | 轻松高效的C语言编译工具介绍
  • 企业网站设计特点建设银行网站怎么打印明细
  • C语言练习题——判断水仙花数(0-100000)
  • 广州好的网站建设昆明微网站搭建哪家好
  • 找国内外贸公司的网站深圳自己做网站
  • 视频融合平台EasyCVR:云台控制与语音对讲赋能远程交互式视频监控新场景
  • 做设计私活的网站php网站建设费用
  • next.js(二)——从react到next.js
  • Android开发自学笔记 --- 构建简单的UI视图
  • ubuntu 升级mysql由mysql5.7.42 升级到8.4.0
  • 项目实战Now in Android:项目模块说明
  • 自己做店铺网站宁波建设有限公司
  • 激活函数是什么,神经网络中为什么要有激活函数
  • 全面且详细地解析神经网络中梯度下降(Gradient Descent, GD)的原理
  • 软考 系统架构设计师历年真题集萃(200)—— 2025年11月系统架构设计师真题3
  • 上海网站建设公司sky建筑装修装饰工程内容
  • 网站建设平台接单周到的商城网站建设
  • MySQL快速入门——使用C_C++连接
  • 机器人教师的课堂管理权限边界讨论:会替代人类教师吗?
  • 南昌网站seo技术厂家网站建设flash
  • 库存周转天数、库存周转率和安全库存如何计算和设定?
  • 福建省建设监理公司网站wordpress 变小程序