什么是AutoreleasePool
一听到AutoreleasePool,有些同学可能会与MRC联系起来,其实它并不是属于MRC时代的,当你创建一个项目的时候,可以去main.m看下,是用AutoreleasePool包起来了;还有当我们写一个for循环的时候,考虑到内存可以及时释放的时候我们也会主动加上AutoreleasePool,所以ARC也是可以用AutoreleasePool的。
Main函数
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
@autoreleasepool到底是什么东西呢,也不能Jump to definition,我们还是通过命令
clang -rewrite-objc main.m
转成c++代码来看下吧:#ifndef __OBJC2__ #define __OBJC2__ #endif struct objc_selector; struct objc_class; struct __rw_objc_super { struct objc_object *object; struct objc_object *superClass; __rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} }; #ifndef _REWRITER_typedef_Protocol typedef struct objc_object Protocol; #define _REWRITER_typedef_Protocol #endif #define __OBJC_RW_DLLIMPORT extern __OBJC_RW_DLLIMPORT void objc_msgSend(void); __OBJC_RW_DLLIMPORT void objc_msgSendSuper(void); __OBJC_RW_DLLIMPORT void objc_msgSend_stret(void); __OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void); __OBJC_RW_DLLIMPORT void objc_msgSend_fpret(void); __OBJC_RW_DLLIMPORT struct objc_class *objc_getClass(const char *); __OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass(struct objc_class *); __OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass(const char *); __OBJC_RW_DLLIMPORT void objc_exception_throw( struct objc_object *); __OBJC_RW_DLLIMPORT int objc_sync_enter( struct objc_object *); __OBJC_RW_DLLIMPORT int objc_sync_exit( struct objc_object *); __OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *); #ifdef _WIN64 typedef unsigned long long _WIN_NSUInteger; #else typedef unsigned int _WIN_NSUInteger; #endif #ifndef __FASTENUMERATIONSTATE struct __objcFastEnumerationState { unsigned long state; void **itemsPtr; unsigned long *mutationsPtr; unsigned long extra[5]; }; __OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *); #define __FASTENUMERATIONSTATE #endif #ifndef __NSCONSTANTSTRINGIMPL struct __NSConstantStringImpl { int *isa; int flags; char *str; #if _WIN64 long long length; #else long length; #endif }; #ifdef CF_EXPORT_CONSTANT_STRING extern "C" __declspec(dllexport) int __CFConstantStringClassReference[]; #else __OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[]; #endif #define __NSCONSTANTSTRINGIMPL #endif #ifndef BLOCK_IMPL #define BLOCK_IMPL struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; // Runtime copy/destroy helper functions (from Block_private.h) #ifdef __OBJC_EXPORT_BLOCKS extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int); extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int); extern "C" __declspec(dllexport) void *_NSConcreteGlobalBlock[32]; extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32]; #else __OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int); __OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int); __OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32]; __OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32]; #endif #endif #define __block #define __weak #include <stdarg.h> struct __NSContainer_literal { void * *arr; __NSContainer_literal (unsigned int count, ...) { va_list marker; va_start(marker, count); arr = new void *[count]; for (unsigned i = 0; i < count; i++) arr[i] = va_arg(marker, void *); va_end( marker ); }; ~__NSContainer_literal() { delete[] arr; } }; extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void); extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *); struct __AtAutoreleasePool { __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();} ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);} void * atautoreleasepoolobj; }; #define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER) int main(int argc, char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; return 0; } } static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
几行代码就转成了这么多代码,看到main函数的AutoreleasePool块变成了
__AtAutoreleasePool __autoreleasepool;
,然而__AtAutoreleasePool是一个结构体,里面包含一个构造函数与一个析构函数,分别调用了objc_autoreleasePoolPush
与objc_autoreleasePoolPop
,这2个定义不在这边,是在NSObject中 源码地址void * objc_autoreleasePoolPush(void) { return AutoreleasePoolPage::push(); } void objc_autoreleasePoolPop(void *ctxt) { AutoreleasePoolPage::pop(ctxt); } void * _objc_autoreleasePoolPush(void) { return objc_autoreleasePoolPush(); } void _objc_autoreleasePoolPop(void *ctxt) { objc_autoreleasePoolPop(ctxt); } void _objc_autoreleasePoolPrint(void) { AutoreleasePoolPage::printAll(); }
通过以上函数代码可以看到push与pop都是通过
AutoreleasePoolPage
来管理的,我们来看看AutoreleasePoolPage
的结构,它是一个class,代码有点长class AutoreleasePoolPage { // EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is // pushed and it has never contained any objects. This saves memory // when the top level (i.e. libdispatch) pushes and pops pools but // never uses them. # define EMPTY_POOL_PLACEHOLDER ((id*)1) # define POOL_BOUNDARY nil static pthread_key_t const key = AUTORELEASE_POOL_KEY; static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing static size_t const SIZE = #if PROTECT_AUTORELEASEPOOL PAGE_MAX_SIZE; // must be multiple of vm page size #else PAGE_MAX_SIZE; // size and alignment, power of 2 #endif static size_t const COUNT = SIZE / sizeof(id); magic_t const magic;//检查校验完整性的变量 id *next;//next指向栈顶的最新的autorelease对象的下一个位置 pthread_t const thread;//当前所所在线程 AutoreleasePoolPage * const parent; AutoreleasePoolPage *child; uint32_t const depth;//深度 uint32_t hiwat;//high water mark 数据容纳的一个上限 // SIZE-sizeof(*this) bytes of contents follow static void * operator new(size_t size) { return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE); } static void operator delete(void * p) { return free(p); } inline void protect() { #if PROTECT_AUTORELEASEPOOL mprotect(this, SIZE, PROT_READ); check(); #endif } inline void unprotect() { #if PROTECT_AUTORELEASEPOOL check(); mprotect(this, SIZE, PROT_READ | PROT_WRITE); #endif } AutoreleasePoolPage(AutoreleasePoolPage *newParent) : magic(), next(begin()), thread(pthread_self()), parent(newParent), child(nil), depth(parent ? 1+parent->depth : 0), hiwat(parent ? parent->hiwat : 0) { if (parent) { parent->check(); assert(!parent->child); parent->unprotect(); parent->child = this; parent->protect(); } protect(); } ~AutoreleasePoolPage() { check(); unprotect(); assert(empty()); // Not recursive: we don't want to blow out the stack // if a thread accumulates a stupendous amount of garbage assert(!child); } void busted(bool die = true) { magic_t right; (die ? _objc_fatal : _objc_inform) ("autorelease pool page %p corrupted\n" " magic 0x%08x 0x%08x 0x%08x 0x%08x\n" " should be 0x%08x 0x%08x 0x%08x 0x%08x\n" " pthread %p\n" " should be %p\n", this, magic.m[0], magic.m[1], magic.m[2], magic.m[3], right.m[0], right.m[1], right.m[2], right.m[3], this->thread, pthread_self()); } void check(bool die = true) { if (!magic.check() || !pthread_equal(thread, pthread_self())) { busted(die); } } void fastcheck(bool die = true) { #if CHECK_AUTORELEASEPOOL check(die); #else if (! magic.fastcheck()) { busted(die); } #endif } id * begin() { return (id *) ((uint8_t *)this+sizeof(*this)); } id * end() { return (id *) ((uint8_t *)this+SIZE); } bool empty() { return next == begin(); } bool full() { return next == end(); } bool lessThanHalfFull() { return (next - begin() < (end() - begin()) / 2); } id *add(id obj) { assert(!full()); unprotect(); id *ret = next; // faster than `return next-1` because of aliasing *next++ = obj; protect(); return ret; } void releaseAll() { releaseUntil(begin()); } 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 } void kill() { // Not recursive: we don't want to blow out the stack // if a thread accumulates a stupendous amount of garbage 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); } static void tls_dealloc(void *p) { if (p == (void*)EMPTY_POOL_PLACEHOLDER) { // No objects or pool pages to clean up here. return; } // reinstate TLS value while we work setHotPage((AutoreleasePoolPage *)p); if (AutoreleasePoolPage *page = coldPage()) { if (!page->empty()) pop(page->begin()); // pop all of the pools if (DebugMissingPools || DebugPoolAllocation) { // pop() killed the pages already } else { page->kill(); // free all of the pages } } // clear TLS value so TLS destruction doesn't loop setHotPage(nil); } static AutoreleasePoolPage *pageForPointer(const void *p) { return pageForPointer((uintptr_t)p); } static AutoreleasePoolPage *pageForPointer(uintptr_t p) { AutoreleasePoolPage *result; uintptr_t offset = p % SIZE; assert(offset >= sizeof(AutoreleasePoolPage)); result = (AutoreleasePoolPage *)(p - offset); result->fastcheck(); return result; } static inline bool haveEmptyPoolPlaceholder() { id *tls = (id *)tls_get_direct(key); return (tls == EMPTY_POOL_PLACEHOLDER); } static inline id* setEmptyPoolPlaceholder() { assert(tls_get_direct(key) == nil); tls_set_direct(key, (void *)EMPTY_POOL_PLACEHOLDER); return EMPTY_POOL_PLACEHOLDER; } 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 inline void setHotPage(AutoreleasePoolPage *page) { if (page) page->fastcheck(); tls_set_direct(key, (void *)page); } static inline AutoreleasePoolPage *coldPage() { AutoreleasePoolPage *result = hotPage(); if (result) { while (result->parent) { result = result->parent; result->fastcheck(); } } return result; } 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) { // "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; if (haveEmptyPoolPlaceholder()) { // We are pushing a second pool over the empty placeholder pool // or pushing the first object into the empty placeholder pool. // Before doing that, push a pool boundary on behalf of the pool // that is currently represented by the empty placeholder. pushExtraBoundary = true; } else if (obj != POOL_BOUNDARY && DebugMissingPools) { // We are pushing an object with no pool in place, // and no-pool debugging was requested by environment. _objc_inform("MISSING POOLS: (%p) Object %p of class %s " "autoreleased with no pool in place - " "just leaking - break on " "objc_autoreleaseNoPool() to debug", pthread_self(), (void*)obj, object_getClassName(obj)); objc_autoreleaseNoPool(obj); return nil; } else if (obj == POOL_BOUNDARY && !DebugPoolAllocation) { // We are pushing a pool with no pool in place, // and alloc-per-pool debugging was not requested. // Install and return the empty pool placeholder. return setEmptyPoolPlaceholder(); } // 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); } static __attribute__((noinline)) id *autoreleaseNewPage(id obj) { AutoreleasePoolPage *page = hotPage(); if (page) return autoreleaseFullPage(obj, page); else return autoreleaseNoPage(obj); } public: 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 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; } static void badPop(void *token) { // Error. For bincompat purposes this is not // fatal in executables built with old SDKs. if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)) { // OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal. _objc_fatal ("Invalid or prematurely-freed autorelease pool %p.", token); } // Old SDK. Bad pop is warned once. static bool complained = false; if (!complained) { complained = true; _objc_inform_now_and_on_crash ("Invalid or prematurely-freed autorelease pool %p. " "Set a breakpoint on objc_autoreleasePoolInvalid to debug. " "Proceeding anyway because the app is old " "(SDK version " SDK_FORMAT "). Memory errors are likely.", token, FORMAT_SDK(sdkVersion())); } objc_autoreleasePoolInvalid(token); } 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(); } } } static void init() { int r __unused = pthread_key_init_np(AutoreleasePoolPage::key, AutoreleasePoolPage::tls_dealloc); assert(r == 0); } void print() { _objc_inform("[%p] ................ PAGE %s %s %s", this, full() ? "(full)" : "", this == hotPage() ? "(hot)" : "", this == coldPage() ? "(cold)" : ""); check(false); for (id *p = begin(); p < next; p++) { if (*p == POOL_BOUNDARY) { _objc_inform("[%p] ################ POOL %p", p, p); } else { _objc_inform("[%p] %#16lx %s", p, (unsigned long)*p, object_getClassName(*p)); } } } static void printAll() { _objc_inform("##############"); _objc_inform("AUTORELEASE POOLS for thread %p", pthread_self()); AutoreleasePoolPage *page; ptrdiff_t objects = 0; for (page = coldPage(); page; page = page->child) { objects += page->next - page->begin(); } _objc_inform("%llu releases pending.", (unsigned long long)objects); if (haveEmptyPoolPlaceholder()) { _objc_inform("[%p] ................ PAGE (placeholder)", EMPTY_POOL_PLACEHOLDER); _objc_inform("[%p] ################ POOL (placeholder)", EMPTY_POOL_PLACEHOLDER); } else { for (page = coldPage(); page; page = page->child) { page->print(); } } _objc_inform("##############"); } static void printHiwat() { // Check and propagate high water mark // Ignore high water marks under 256 to suppress noise. AutoreleasePoolPage *p = hotPage(); uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin()); if (mark > p->hiwat && mark > 256) { for( ; p; p = p->parent) { p->unprotect(); p->hiwat = mark; p->protect(); } _objc_inform("POOL HIGHWATER: new high water mark of %u " "pending releases for thread %p:", mark, pthread_self()); void *stack[128]; int count = backtrace(stack, sizeof(stack)/sizeof(stack[0])); char **sym = backtrace_symbols(stack, count); for (int i = 0; i < count; i++) { _objc_inform("POOL HIGHWATER: %s", sym[i]); } free(sym); } } #undef POOL_BOUNDARY };
首先,我们来看下它的属性,看到有个parent与child,证明它是一个双向链表:
它有个
static size_t const SIZE = PAGE_MAX_SIZE;
(PAGE_MAX_SIZE
定义是在usr/include/mach/i386/vm_param.h中)#define PAGE_MAX_SIZE PAGE_SIZE #define PAGE_SIZE I386_PGBYTES #define I386_PGBYTES 4096 /* bytes per 80386 page */
OK,所以每个AutoreleasePoolPage的大小都是一样的,都是4096
相关操作
- begin() 表示了一个AutoreleasePoolPage节点开始存autorelease对象的位置。
- end() 一个AutoreleasePoolPage节点最大的位置
- empty() 如果next指向beigin()说明为空
- full() 如果next指向end说明满了
- id *add(id obj) 添加一个autorelease对象,next指向下一个存对象的地址。
一个空的AutoreleasePoolPage大致如下:
关键词
EMPTY_POOL_PLACEHOLDER
- 当一个释放池没有包含任何对象,又刚好被推入栈中,就存储在TLS(
Thread_local_storage
)中,叫做空池占位符POOL_BOUNDARY
- 边界对象,代表 AutoreleasePoolPage 中的第一个对象
关键函数:Push与Pop
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; }
若DebugPoolAllocation=true则需要每个pool都生成一个新的page,否则就执行autoreleaseFast
autoreleaseNewPage
id *autoreleaseNewPage(id obj) { AutoreleasePoolPage *page = hotPage(); if (page) return autoreleaseFullPage(obj, page); else return autoreleaseNoPage(obj); }
- 当前存在page执行autoreleaseFullPage方法;
- 当前不存在pageautoreleaseNoPage方法。
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方法。
hotPage
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; }
可以看到hotPage是存在TLS(
Thread_local_storage
)线程私有数据里面的autoreleaseFullPage
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); }
autoreleaseFullPage整个过程就是遍历传入的page双向链表,若page满了,则往child查找,直到找到一个未满的page,然后会将obj添加到这个page;当然有可能找到最后一个page都满了的情况,那么这个page的child肯定是空的,就会取执行
page = new AutoreleasePoolPage(page);
生成一个新的page来使用。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; if (haveEmptyPoolPlaceholder()) { // We are pushing a second pool over the empty placeholder pool // or pushing the first object into the empty placeholder pool. // Before doing that, push a pool boundary on behalf of the pool // that is currently represented by the empty placeholder. pushExtraBoundary = true; } else if (obj != POOL_BOUNDARY && DebugMissingPools) { // We are pushing an object with no pool in place, // and no-pool debugging was requested by environment. _objc_inform("MISSING POOLS: (%p) Object %p of class %s " "autoreleased with no pool in place - " "just leaking - break on " "objc_autoreleaseNoPool() to debug", pthread_self(), (void*)obj, object_getClassName(obj)); objc_autoreleaseNoPool(obj); return nil; } else if (obj == POOL_BOUNDARY && !DebugPoolAllocation) { // We are pushing a pool with no pool in place, // and alloc-per-pool debugging was not requested. // Install and return the empty pool placeholder. return setEmptyPoolPlaceholder(); } // 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); }
- NoPage就是没有pool被push过或者是push了一个空的pool
- 新创建一个AutoreleasePoolPage,并设置hotPage
- 若push了一个空的pool则会先添加一个边界对象
POOL_BOUNDARY
Autorelease
// Replaced by ObjectAlloc - (id)autorelease { return ((id)self)->rootAutorelease(); } * // Base autorelease implementation, ignoring overrides. 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; }
简单一句话,就是最终调用了autoreleaseFast,逻辑很明了,当然它这个流程里面也判断了是否是伪指针,关于伪指针的介绍,不了解的可以翻翻之前的博客,已经介绍过了,这边就不再赘述。
Pop
void objc_autoreleasePoolPop(void *ctxt) { AutoreleasePoolPage::pop(ctxt); } 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(); } } } static inline AutoreleasePoolPage *coldPage() { AutoreleasePoolPage *result = hotPage(); if (result) { while (result->parent) { result = result->parent; result->fastcheck(); } } return result; }
上面的pop可以认为分三种case
- case1:token == (void*)EMPTY_POOL_PLACEHOLDER
- 空pool,直接pop最顶层的page
- 若存在hotPage则递归coldPage
- 否则setHotPage(nil)
- case2:MRC下,没有使用autoreleasepool,直接调用了autorelease
- 此时因为没有autoreleasepool,所以直接pop
- case3:我们常用的方式
- 关键函数kill()
kill()
void kill() { // Not recursive: we don't want to blow out the stack // if a thread accumulates a stupendous amount of garbage 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); } inline void unprotect() { #if PROTECT_AUTORELEASEPOOL check(); mprotect(this, SIZE, PROT_READ | PROT_WRITE); #endif } inline void protect() { #if PROTECT_AUTORELEASEPOOL mprotect(this, SIZE, PROT_READ); check(); #endif }
- 遍历page,有child,就page重置为child,直到没有
- 还是遍历page,page重置为parent,释放child
AutoreleasePool、Runloop、线程之间的关系
苹果官方有文档说明:
Each NSThread object, including the application’s main thread, has an NSRunLoop object automatically created for it as needed.
The Application Kit creates an autorelease pool on the main thread at the beginning of every cycle of the event loop, and drains it at the end, thereby releasing any autoreleased objects generated while processing an event.
Each thread (including the main thread) maintains its own stack of NSAutoreleasePool objects.
中文意思如下:
- 每一个线程(含主线程)都会拥有一个专属的runloop,并且会在有需要的时候自动创建。
- 系统在主线程的runloop开始之前会自动创建一个autorelease pool,在结束的时候会自动pop。
- 每个线程都会维护自己NSAutoreleasePool的对象堆栈。
伪代码Runloop与AutoreleasePool执行
runloop entry context = autoreleasepage->push(xxx) ... ... autoreloeasepage->pop(context) runloop exit