定义
AutoReleasePool是Objective—C中一种内存自动回收的机制,他可以将加入AutoReleasePool中的变量release的时机延迟。也就是说,当你创建一个对象,在正常情况下,变量会在超出其作用域的时候立即release,如果将该对象加入到自动释放池中,这个对象并不会立即释放,而是等到runloop休眠或者超出AutoReleasePool的作用域{}之后才会被释放。
本质:
autorelease本质就是延迟调用release方法
MRC环境下,通过[obj autorelease]来延迟内存的释放
ARC环境下,是不能手动调用,系统会自动给对象添加autorelease
自动释放池的主要底层数据结构是:__AtAutoreleasePool、AutoreleasePoolPage
调用了autorelease的对象最终都是通过AutoreleasePoolPage对象来管理的
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int main(int argc, char * argv[]) { @autoreleasepool { NSObject * obj = [[NSObject alloc] init]; } } int main(int argc, char * argv[]) { { __AtAutoreleasePool __autoreleasepool; NSObject * obj = ((NSObject *(*)(id , SEL))(void *)objc_msgSend)((id )((NSObject *(*)(id , SEL))(void *)objc_msgSend)((id )objc_getClass("NSObject" ), sel_registerName("alloc" )), sel_registerName("init" )); } } int main (int argc, char * argv[]) {void *poolToken = objc_autoreleasePoolPush(); 这中间为写在{...}中的代码 objc_autoreleasePoolPop(哨兵对象地址); }
源码分析
AutoreleasePoolPage
每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址
所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class AutoreleasePoolPage { PAGE_MAX_SIZE; magic_t const magic; id *next; pthread_t const thread; AutoreleasePoolPage * const parent; AutoreleasePoolPage *child; uint32_t const depth; }
NSObject.mm - push
objc_autoreleasePoolPush
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 void *objc_autoreleasePoolPush (void ) { return AutoreleasePoolPage::push (); } static inline void *push () { id *dest; if (DebugPoolAllocation) { 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); } }
NSObject.mm - pop 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 void objc_autoreleasePoolPop (void *ctxt) { AutoreleasePoolPage::pop (ctxt); } static inline void pop (void *token) { AutoreleasePoolPage *page; id *stop; if (token == (void *)EMPTY_POOL_PLACEHOLDER) { if (hotPage ()) { pop (coldPage ()->begin ()); } else { setHotPage (nil); } return ; } page = pageForPointer (token); stop = (id *)token; if (PrintPoolHiwat) printHiwat (); page->releaseUntil (stop); if (DebugPoolAllocation && page->empty ()) { AutoreleasePoolPage *parent = page->parent; page->kill (); setHotPage (parent); } else if (DebugMissingPools && page->empty () && !page->parent) { page->kill (); setHotPage (nil); } else if (page->child) { if (page->lessThanHalfFull ()) { page->child->kill (); } else if (page->child->child) { page->child->child->kill (); } } }
autoreleasePool的结构和工作原理
autoreleasepool本质上就是一个指针堆栈,内部结构是由若干个以AutoreleasePoolPage对象为结点的双向链表组成,系统会在需要的时候动态地增加或删除page节点。
push,pop大体流程
调用push方法会将一个POOL_BOUNDARY入栈,并且返回其存放的内存地址
调用pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY
id *next指向了下一个能存放autorelease对象地址的区域
Runloop和Autorelease
iOS在主线程的Runloop中注册了2个Observer
第1个Observer监听了kCFRunLoopEntry事件,会调用objc_autoreleasePoolPush()
第2个Observer
监听了kCFRunLoopBeforeWaiting事件,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
监听了kCFRunLoopBeforeExit事件,会调用objc_autoreleasePoolPop()
参考