自动释放池
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
- objc_autoreleasePoolPush
- objc_autoreleasePoolPop
- 结构体中有一个构造函数和析构函数
objc_autoreleasePoolPush
Autorelease池实现
线程的自动释放池是指针的堆栈。
每个指针要么是一个要释放的对象,要么是POOL_BOUNDARY
自动发布池边界。
池令牌是指向该池的POOL_BOUNDARY的指针。当
池被弹出,每个比哨兵温度高的对象都被释放。
堆栈被划分为一个双链接的页面列表。页面添加
必要时删除。
线程本地存储指向热页面,其中新自动释放
对象存储。
AutoreleasePoolPage
AutoreleasePoolPageData
magic : 用来校验AutoreleasePoolPage的结构是否完整
next : 指向最新添加的autoleased对象的下一个位置,初始化时指向begin()
thread : 指向当前线程
parent : 指向父节点,第一个节点的parent值为nil
child : 指向子节点,最后一个节点的child值为nil
depth : 代表深度,从0开始往后递增1
hiwat : 代表high water makrk 最大入栈数量标记
-
工程中关闭ARC
-
编写代码运行调试
自动释放池的创建和压栈
-
push
这里在构造压栈的第一个就放入了一个对象是pool_boundary,是一个哨兵对象。
-
autoreleaseFast
-
autoreleaseNoPage
page->add(POOL_BOUNDARY);加入哨兵对象
- 双向链表的构造
- begin() 开辟内存
这里的this的内存大小是56
magic = 16,
next = 8,
thread = 8,
parent= 8,
child = 8,
depth = 4,
hiwat = 4,
这里的static 是全局区所以这个结构体的大小是 4*4的数组 是16 字节
所以这里符合 56字节的内存大小
-
objc_thread_self() 获取当前线程
-
重新回看这个结果打印
这里可以清晰的发现在地址 0x10180b000处开始,0x10180b038这里加入的是一个哨兵对象,中间差了56个字节,在哨兵对象后才加入了第一个NSObject对象0x10180b040。
-
autoreleaseFullPage
这里是如果自动释放池满了就会一直地轨到最后一页创建新的page,设置为hote
-
创建的结构示意图如图所示
自动释放池的满页临界点
是1左移12位即2的12次方为4096.为4K.
- 测试
这里循环压栈如505个对象,发现这里新创建了page,这里可以计算验证504个对象+1个哨兵对象+56字节的默认对象 504*8+8+56=4096.同时也发现了在创建了新的page后是不在会创建哨兵对象的。所以哨兵对象是只有一个的。
自动释放池出栈
链表结构向上翻页将父页设置为Hot,调用kill删除本页。
do-while循环不断的向前删除。
遇到哨兵边界后停止pop.