iOS之自动释放池 二

258 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情

对象压栈

进入objc_autoreleasePoolPush函数

void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

进入AutoreleasePoolPage命名空间下的push函数

static inline void *push() 
{
    id *dest;
    if (slowpath(DebugPoolAllocation)) {
        // Each autorelease pool starts on a new pool page.
        dest = autoreleaseNewPage(POOL_BOUNDARY);
    } else {
        dest = autoreleaseFast(POOL_BOUNDARY);
    }
    ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
    return dest;
}
  • DebugPoolAllocation:当自动释放池按顺序弹出时停止,并允许堆调试器跟踪自动释放池
  • 不存在,调用autoreleaseNewPage函数,从一个新的池页开始创建
  • 否则,调用autoreleaseFast函数,将哨兵对象压栈
autoreleaseFast

进入autoreleaseFast函数

static inline id *autoreleaseFast(id obj)
{
    AutoreleasePoolPage *page = hotPage();
    if (page && !page->full()) {
        return page->add(obj);
    } else if (page) {
        return autoreleaseFullPage(obj, page);
    } else {
        return autoreleaseNoPage(obj);
    }
}
  • 如果存在page,并且没有存满,调用add函数
  • 如果存在page,但存储已满,调用autoreleaseFullPage函数
  • 否则,不存在page,调用autoreleaseNoPage函数
autoreleaseNoPage

进入autoreleaseNoPage函数

static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
    // "No page" could mean no pool has been pushed
    // or an empty placeholder pool has been pushed and has no contents yet
    ASSERT(!hotPage());

    bool pushExtraBoundary = false;
    
    ...

    // We are pushing an object or a non-placeholder'd pool.

    // Install the first page.
    AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
    setHotPage(page);
    
    // Push a boundary on behalf of the previously-placeholder'd pool.
    if (pushExtraBoundary) {
        page->add(POOL_BOUNDARY);
    }
    
    // Push the requested object or pool.
    return page->add(obj);
}
  • 调用AutoreleasePoolPage构造函数,创建新页
  • 设置为热页面
  • pushExtraBoundaryYES,哨兵对象压栈
  • 对象压栈 进入AutoreleasePoolPage构造函数
AutoreleasePoolPage(AutoreleasePoolPage *newParent) :
    AutoreleasePoolPageData(begin(),
                            objc_thread_self(),
                            newParent,
                            newParent ? 1+newParent->depth : 0,
                            newParent ? newParent->hiwat : 0)
{
    if (objc::PageCountWarning != -1) {
        checkTooMuchAutorelease();
    }

    if (parent) {
        parent->check();
        ASSERT(!parent->child);
        parent->unprotect();
        parent->child = this;
        parent->protect();
    }
    protect();
}
  • 通过父类AutoreleasePoolPageData进行初始化
  • begin:获取对象压栈的起始位置
  • objc_thread_self:通过tls获取当前线程
  • 链接双向链表 进入begin函数
id * begin() {
    return (id *) ((uint8_t *)this+sizeof(*this));
}
  • sizeof(*this):大小取决于自身结构体中的成员变量
  • 返回对象可压栈的真正开始地址,在成员变量以下 进入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;

	...
};
  • magic16字节
  • nextthreadparentchild:各占8字节
  • depthhiwat:各占4字节
  • 共占56字节 进入magic_t的定义
struct magic_t {
	static const uint32_t M0 = 0xA1A1A1A1;
#   define M1 "AUTORELEASE!"
	static const size_t M1_len = 12;
	uint32_t m[4];

	...

#   undef M1
};
  • 结构体的创建在堆区申请内存,而静态成员存储在静态区,不占结构体大小
  • 16字节来自于uint32_t数组 进入objc_thread_self函数
static inline pthread_t objc_thread_self()
{
    return (pthread_t)tls_get_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF);
}
  • 通过tls获取当前线程
autoreleaseFullPage

进入autoreleaseFullPage函数

id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
    // The hot page is full. 
    // Step to the next non-full page, adding a new page if necessary.
    // Then add the object to that page.
    ASSERT(page == hotPage());
    ASSERT(page->full()  ||  DebugPoolAllocation);

    do {
        if (page->child) page = page->child;
        else page = new AutoreleasePoolPage(page);
    } while (page->full());

    setHotPage(page);
    return page->add(obj);
}
  • 遍历链表,找到最后一个空白的子页面
  • 对其进行创建新页
  • 设置为热页面
  • 添加对象 形成以下数据结构:

image-5.png

add

进入add函数

id *add(id obj)
{
    ASSERT(!full());
    unprotect();
    id *ret;

	...
        
    ret = next;  // faster than `return next-1` because of aliasing
    *next++ = obj;
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
    // Make sure obj fits in the bits available for it
    ASSERT(((AutoreleasePoolEntry *)ret)->ptr == (uintptr_t)obj);
#endif
 done:
    protect();
    return ret;
}
  • 使用*next++进行内存平移
  • 将对象压栈
autoreleaseNewPage

进入autoreleaseNewPage函数

id *autoreleaseNewPage(id obj)
{
    AutoreleasePoolPage *page = hotPage();
    if (page) return autoreleaseFullPage(obj, page);
    else return autoreleaseNoPage(obj);
}
  • 获取热页面
  • 存在,调用autoreleaseFullPage函数
  • 否则,不存在page,调用autoreleaseNoPage函数