对于autoreleasepool
一定不陌生,但内部结构是什么呢?
int main(int argc, const char * argv[]) {
@autoreleasepool {
}
return 0;
}
@autoreleasepool
做了什么操作呢?再次祭出clang
clang命令: clang -rewrite-objc main.m -o main.cpp
int main(int argc, const char * argv[]) {
{ __AtAutoreleasePool __autoreleasepool; }
return 0;
}
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
autoreleasepool在经过clang的编译之后
,底层的实现是一个__AtAutoreleasePool结构体
;objc_autoreleasePoolPush()
,析构函数objc_autoreleasePoolPop(atautoreleasepoolobj)
;这三个点就是探索的入口;class AutoreleasePoolPage : private AutoreleasePoolPageData
{
friend struct thread_data_t;
//一页大小4098
#define PAGE_MIN_SIZE (1 << PAGE_MIN_SHIFT)
static size_t const SIZE = PAGE_MAX_SIZE
// 构造函数
AutoreleasePoolPage(AutoreleasePoolPage *newParent) :
AutoreleasePoolPageData(begin(),
objc_thread_self(),
newParent,
newParent ? 1+newParent->depth : 0,
newParent ? newParent->hiwat : 0)
{
...
}
// 析构函数
~AutoreleasePoolPage()
{...}
}
thread_data_t
保存当前线程信息,可以看到自动释放池与线程是有关的;4096字节
,后面会验证这部分;构造函数
中newParent=nil
也就是AutoreleasePoolPageData(begin(), objc_thread_self(),nil newParent, 0, 0)
/***********************************************************************
Autorelease pool implementation
A thread's autorelease pool is a stack of pointers.
Each pointer is either an object to release, or POOL_BOUNDARY which is
an autorelease pool boundary.
A pool token is a pointer to the POOL_BOUNDARY for that pool. When
the pool is popped, every object hotter than the sentinel is released.
The stack is divided into a doubly-linked list of pages. Pages are added
and deleted as necessary.
Thread-local storage points to the hot page, where newly autoreleased
objects are stored.
**********************************************************************/
struct AutoreleasePoolPageData
{
magic_t const magic;
__unsafe_unretained id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
//对照:`AutoreleasePoolPageData(begin(), objc_thread_self(),nil newParent, 0, 0)`
AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
: magic(), next(_next), thread(_thread),
parent(_parent), child(nil),
depth(_depth), hiwat(_hiwat)
{
}
};
autoreleasepool特点:
child和parent
就说明AutoreleasePoolPageData是一个双向链表
;结构图:
销毁对象
:就是在autoreleasepool
中声明的对象,每次入栈一个对象_next就会往后移动8个字节
;hotPage()
,其他页标记为coldPage()
;在clang代码查看到的:* objc_autoreleasePoolPush()
,objc_autoreleasePoolPop(atautoreleasepoolobj)
源码调用流程,只看主分支代码:objc_autoreleasePoolPush -> AutoreleasePoolPage::push() -> autoreleaseFast(POOL_BOUNDARY) -> autoreleaseFast
POOL_BOUNDARY nil 哨兵对象 POOL_BOUNDARY在每一个autoreleasepool第一页的第一个对象插入,防止在释放时越界;
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
// 1. 如果不满,则直接插入
if (page && !page->full()) {
return page->add(obj);
} else if (page) { // 2. 如果满了,创建新页并将当前页传入建立关系
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
id *add(id obj)
{
id *ret = next; // faster than `return next-1` because of aliasing
// 对象赋值,并移动next
*next++ = obj;
return ret;
}
总体流程:
第一次 push 方法
是创建一个页,并设置为当前的焦点页(hotPage)
,并且把哨兵放进去做栈底
。焦点页已满
,会递归子页去找到未满的页来入栈对象
;如果所有页都满了,会再创建一个页,并将上一个页的child 指向这个新的页,然后把新的页设置为焦点页
。源码调用流程,只看主分支代码:objc_autoreleasePoolPop -> AutoreleasePoolPage::pop(ctxt) -> popPage<false>(token, page, stop)
template<bool allowDebug>
static void
popPage(void *token, AutoreleasePoolPage *page, id *stop)
{
// 一口气释放页内所有对象
page->releaseUntil(stop);
// 释放页
// memory: delete empty children
if (allowDebug && DebugPoolAllocation && page->empty()) {
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (allowDebug && DebugMissingPools && page->empty() && !page->parent) {
// special case: delete everything for pop(top)
// when debugging missing autorelease pools
page->kill();
setHotPage(nil);
} else if (page->child) {
// hysteresis: keep one empty child if page is more than half full
if (page->lessThanHalfFull()) {
page->child->kill();
}
else if (page->child->child) {
page->child->child->kill();
}
}
}
void kill()
{
AutoreleasePoolPage *page = this;
while (page->child) page = page->child;
AutoreleasePoolPage *deathptr;
do {
deathptr = page;
page = page->parent;
if (page) {
page->unprotect();
page->child = nil;
page->protect();
}
delete deathptr;
} while (deathptr != this);
}
void releaseUntil(id *stop)
{
while (this->next != stop) {
// 获取当前页,当前页有可能是空的
AutoreleasePoolPage *page = hotPage();
//找到非空页
while (page->empty()) {
page = page->parent;
// 拿到当前页的父类,并设为当前页
setHotPage(page);
}
page->unprotect();
// 找到前一个要释放的对象
id obj = *--page->next;
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
// 如果是哨兵则释放完成
if (obj != POOL_BOUNDARY) {
//// 如果不是哨兵对象则继续释放
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
for (int i = 0; i < count + 1; i++) {
objc_release(obj);
}
#else
objc_release(obj);
#endif
}
}
setHotPage(this);
}
释放到第一页的哨兵对象
结束;补充:
_objc_autoreleasePollPrint()