一、@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()