一、@autoreleasepool编译后的代码
- 定义
Person类继承自NSObject, 在main函数中代码如下
- 通过终端
cd到main.m所在文件夹, 并执行下面的命令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
- 将生成的
main.cpp文件拖到项目中并打开, 可以找到main.m文件在底层的代码
- 整理后, 就是下面的代码
- 搜索
__AtAutoreleasePool结构体
- 整理后如下
- 我们知道, 一个自动释放池会自动销毁, 所以
main函数中的代码可以模拟为下图所示
- 所以, 想要了解
@autoreleasepool, 可以从objc_autoreleasePoolPush函数和objc_autoreleasePoolPop函数入手
二、objc_autoreleasePoolPush 和 objc_autoreleasePoolPop
objc_autoreleasePoolPush函数的底层实现
objc_autoreleasePoolPop函数的底层实现
- 可以看到
objc_autoreleasePoolPush和objc_autoreleasePoolPop, 都是用了AutoreleasePoolPage - 下图中展示的是
AutoreleasePoolPage中的主要成员变量
自动释放池的主要底层数据结构是:__AtAutoreleasePool、AutoreleasePoolPage
调用了autorelease的对象最终都是通过AutoreleasePoolPage对象来管理的
三、AutoreleasePoolPage的结构
- 每个
AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址
AutoreleasePoolPage中除了一开始的56个字节用来存储成员变量, 其他的所有内存空间都是用来存储被autorelease对象的地址- 其中
begin和end两个函数的实现如下
begin = AutoreleasePoolPage地址 + AutoreleasePoolPage的大小
end = AutoreleasePoolPage地址 + SIZE(4096)
- 当一个
AutoreleasePoolPage不够存储autorelease对象地址时, 就会在创建一个AutoreleasePoolPage - 所有的
AutoreleasePoolPage对象通过双向链表的形式连接在一起
四、AutoreleasePoolPage的push和pop
-
调用
push方法会将一个POOL_BOUNDARY入栈,并且返回其存放的内存地址 -
调用
pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY -
id *next指向了下一个能存放autorelease对象地址的区域 -
可以通过以下私有函数来查看自动释放池的情况
extern void _objc_autoreleasePoolPrint(void); -
使用
_objc_autoreleasePoolPrint函数, 查看一个空的autoreleasepool
- 可以看到, 此时自动缓冲池中并没有任何的
autorelease对象
- 添加一个autorelease对象
- 可以看到
page中有两个缓存, 其中一个POOL就是POOL_BOUNDARY, 第二个就是加入的Person对象
- 再添加一个autorelease对象
- 此时
page中存储三个内容, 一个POOL_BOUNDARY, 两个Person对象
- 嵌套一层
@autoreleasepool, 代码如下
- 此时可以看到
page中存储了两个POOL_BOUNDARY
- 再次嵌套一层
@autoreleasepool, 代码如下
- 此时可以看到
page中存储了三个POOL_BOUNDARY
- 向
@autoreleasepool存放1000个Person的autorelease对象
- 可以看到, 一个
page存储空间用满了, 会再次创建一个page
五、Runloop和Autorelease
- iOS在主线程的
Runloop中注册了2个Observer - 第1个
Observer监听了kCFRunLoopEntry事件,会调用objc_autoreleasePoolPush() - 第2个
Observer - 监听了
kCFRunLoopBeforeWaiting事件,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush() - 监听了
kCFRunLoopBeforeExit事件,会调用objc_autoreleasePoolPop()