源码
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
- 解析:
这个结构体的构造函数里调用objc_autoreleasePoolPush, 析构函数里调用objc_autoreleasePoolPop.那么我们就来分析一下objc_autoreleasePoolPush和objc_autoreleasePoolPop.
push和pop的源码
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
NEVER_INLINE
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
- 解析:
自动释放池的push和pop其实是对AutoreleasePoolPage的push和pop的封装.既然如此,我们就从AutoreleasePoolPage开始看起.
AutoreleasePoolPage
class AutoreleasePoolPage;
struct AutoreleasePoolPageData
{
magic_t const magic; // 4 * 4 = 16
__unsafe_unretained id *next; // 8
pthread_t const thread; // 8
AutoreleasePoolPage * const parent; // 8
AutoreleasePoolPage *child; // 8
uint32_t const depth; // 4
uint32_t hiwat; // 4
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)
{
}
};
#define I386_PGBYTES 4096 /* bytes per 80386 page */
#define PAGE_SIZE I386_PGBYTES
#define PAGE_MAX_SIZE PAGE_SIZE
#define PAGE_MIN_SIZE PAGE_SIZE
- 解析:
从结构可以看出自动释放池是一个由AutoreleasePoolPage组成的双向链表.parent和child就是构造双向链表的指针. AutoreleasePoolPage属性大小为16 + 4 * 8 + 2 * 4 = 56 AutoreleasePoolPage 大小为4096 一页最多存储(4096 - 56) / 8 个临时变量
自动释放池用处
- 产生大量的临时变量时
- 自己创建辅助线程时
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;
}
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);
}
}
static __attribute__((noinline))
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);
}
static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
// ...省略
// 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);
}
id *add(id obj)
{
ASSERT(!full());
unprotect();
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;
protect();
return ret;
}
- 有hotpage并且page没满.
- page->add(obj)把对象加进page里.
- page->add方法其实就是压栈,然后移动栈顶指针.
- page->add(obj)把对象加进page里.
- 有hotpage 并且满了
- autoreleaseFullPage初始化一个新的page并且设置为hotpage
- autoreleaseFullPage方法其实是遍历page直到找到一个没满的fullPage或者创建一个新的page.
- page->add(obj)把对象加进page里
- autoreleaseFullPage初始化一个新的page并且设置为hotpage
- 无hotpage
- autoreleaseNoPage创建一个page
- autoreleaseNoPage表示一个page都没有,会创建第一个page.这个page是没有parent的.
- 设置hotpage
- 加POOL_BOUNDARY进对象page里
- page->add(obj);把对象加进page里
- autoreleaseNoPage创建一个page
pop
static inline void
pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
// ... 省略n多
page = pageForPointer(token);
stop = (id *)token;
return popPage<false>(token, page, stop);
}
template<bool allowDebug>
static void
popPage(void *token, AutoreleasePoolPage *page, id *stop)
{
if (allowDebug && PrintPoolHiwat) printHiwat();
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 releaseUntil(id *stop)
{
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
while (this->next != stop) {
// Restart from hotPage() every time, in case -release
// autoreleased more objects
AutoreleasePoolPage *page = hotPage();
// fixme I think this `while` can be `if`, but I can't prove it
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) {
objc_release(obj);
}
}
setHotPage(this);
#if DEBUG
// we expect any children to be completely empty
for (AutoreleasePoolPage *page = child; page; page = page->child) {
ASSERT(page->empty());
}
#endif
}
- pageForPointer获取当前token所在的AutoreleasePoolPage.
- 通过操作内存地址获得指针的偏移量.(将指针与页面大小4096取模)
- releaseUntil(stop)一直释放栈中的对象直到stop
- 移动栈顶指针, 一个个释放obj直到POOL_BOUNDRY. 一次pop等于是释放POOL_BOUNDRY到POOL_BOUNDRY之间的对象们.
- 一般情况下 stop == token == POOL_BOUNDRY. 其实token可以是别的对象.只不过我们不那么用而已.
Runloop和autoreleasepool的关系
- viewdidload里打断点. po [NSRunloop currentRunLoop];

App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。 第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。
总结
- AutoReleasePool是双向链表. 每一页为一个Page, Page大小为4096.
- 一个Page能存(4096 - 56) / 8 = 505 个变量.
- Runloop有两个Observer监听autoreleasepool.
- Entry时push,
- 休眠时pop和push.
- exit时pop
参考