先对@autoreleasepool { ... }进行clang -rewrite-objc main.m
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
...
}
__AtAutoreleasePool的结构
struct __AtAutoreleasePool {
__AtAutoreleasePool() {//构造函数 在创建结构体的时候调用
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
~__AtAutoreleasePool() {//析构函数,在结构体销毁的时候调用
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
void * atautoreleasepoolobj;
};
从上面可以得出autoreleasePool是这样工作的
atautoreleasepoolobj = objc_autoreleasePoolPush();
调用autorelease变量
objc_autoreleasePoolPop(atautoreleasepoolobj);
在 NSObject.mm 文件中可以找到这两个函数的实现
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
push跟进
static inline void *push()
{
id *dest;
if (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;
}
如果进来没有AutoreleasePoolPage对象,就创建一个AutoreleasePoolPage对象,并插入POOL_BOUNDARY,有的话就直接插入POOL_BOUNDARY
先看下AutoreleasePoolPage的结构
class AutoreleasePoolPage
{
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
}
magic:这个变量的类型是 magic_t,是用来检查 AutoreleasePoolPage 的内存没有被修改的,放在第一个也就是这个原因,防止前面地址有内容溢过来。
next:类型是 id *,存放的是下一个被 autorelease 的对象指针存放的地址。
thread:对应的线程,这说明了自动释放池是对应线程的。
parent 和 child:用来保存前一个 AutoreleasePoolPage 和后一个 AutoreleasePoolPage,就是一个双向链表,毕竟一个 AutoreleasePoolPage 能存放的对象是有限的。
depth:很明显是这个链表有多深。
hiwat:一个在 DEBUG 时才有用的参数,表示最高有记录过多少对象(hi-water)。
static size_t const SIZE =
PAGE_MAX_SIZE; // size and alignment, power of 2
static void * operator new(size_t size) {
return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
}
AutoreleasePoolPage 重载了 new 操作符,这样一个新的对象需要 SIZE 这么多的内存空间,SIZE 的大小 4096字节。AutoreleasePoolPage 的成员变量大小加在一起也只有 56 字节,但是 new 它一个居然要 4096 字节,这剩下的 4040 字节肯定就是存放被 autorelease 的对象的地方了。
AutoreleasePoolPage创建的流程
static __attribute__((noinline))
id *autoreleaseNewPage(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page) return autoreleaseFullPage(obj, page);
else return autoreleaseNoPage(obj);
}
static inline AutoreleasePoolPage *hotPage()
{
AutoreleasePoolPage *result = (AutoreleasePoolPage *)
tls_get_direct(key);
if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
if (result) result->fastcheck();
return result;
}
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 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);
}
}
autoreleaseFast跟上面操作一样,先获取一个AutoreleasePoolPage对象,然后插入POOL_BOUNDARY
autorelease实现
__attribute__((aligned(16)))
id
objc_autorelease(id obj)
{
if (!obj) return obj;
if (obj->isTaggedPointer()) return obj;
return obj->autorelease();
}
inline id
objc_object::autorelease()
{
if (isTaggedPointer()) return (id)this;
if (fastpath(!ISA()->hasCustomRR())) return rootAutorelease();
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, SEL_autorelease);
}
inline id
objc_object::rootAutorelease()
{
if (isTaggedPointer()) return (id)this;
if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
return rootAutorelease2();
}
__attribute__((noinline,used))
id
objc_object::rootAutorelease2()
{
assert(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
static inline id autorelease(id obj)
{
assert(obj);
assert(!obj->isTaggedPointer());
id *dest __unused = autoreleaseFast(obj);
assert(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}
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);
}
}
上面的代码做了一件事情,就是把autorelease的对象page->add(obj)
再来看看pop的实现
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
// Popping the top-level placeholder pool.
if (hotPage()) {
// Pool was used. Pop its contents normally.
// Pool pages remain allocated for re-use as usual.
pop(coldPage()->begin());
} else {
// Pool was never used. Clear the placeholder.
setHotPage(nil);
}
return;
}
page = pageForPointer(token);
stop = (id *)token;
if (*stop != POOL_BOUNDARY) {
if (stop == page->begin() && !page->parent) {
// Start of coldest page may correctly not be POOL_BOUNDARY:
// 1. top-level pool is popped, leaving the cold page in place
// 2. an object is autoreleased with no pool
} else {
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
return badPop(token);
}
}
if (PrintPoolHiwat) printHiwat();
page->releaseUntil(stop);
// memory: delete empty children
if (DebugPoolAllocation && page->empty()) {
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (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
}
上面主要是把autorelease的对象一个一个清空,直到找到POOL_BOUNDARY分割,如果没有找到,就会根据parent找到上一个AutoreleasePoolPage对象,继续清空,知道找到POOL_BOUNDARY,最后把这个空的AutoreleasePoolPage对象也清空掉
网友评论