问题
- 什么是AutoReleasePool ?
- AutoReleasePool 在OC中有哪几种表现形式 ?
- ARC 下AutoReleasePool 规则?
- autoreleasepool 调用流程(释放流程,插入流程)
- Autoreleasepool 什么时候释放
- __autoreleasing 作用
什么是AutoReleasePool
简称自动释放池,拿来进行内存管理优化的,解决内存的峰值 陡增的一种机制。在autoreleasepool 当中创建对象,将对象加到autoreleasepool 当中,当自动释放池销毁的时候(啥时候销毁?)会对所有对象做release 操作。
autoreleasepool 有哪几种表现形式
1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //MRC
id objc = [[NSObject alloc] init];
[objc autorelease];
[pool drain];
2. @autoreleasepool {} // ARC MRC
ARC下的规则
以alloc ,new, copy 的是不会加入到autoreleasepool当中的。释放是以编译器在适当的时候释放的。
autoreleasepool 解析
在我们看原理之前我想请大家思考一个问题:obj1,obj2,obj3什么时候释放?
@autoreleasepool {
NSObject *obj1 = [person person];
@autoreleasepool {
NSObject *obj2 = [person person];
@autoreleasepool {
NSObject *obj3 = [person person];
}
}
}
解析:

Clang :

c++ 源码:

__AtAutoreleasePool :
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
Objc-runtime 源码:
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
AutoreleasePoolPage: (双向链表)
class AutoreleasePoolPage
{
# 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;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
}
可以看出autoreleasepool 是双向链表
同时每一个page 又是一个栈结构
调用流程
插入流程
-
__ AtAutoreleasePool __autoreleasepool 调用构造函数 objc_autoreleasePoolPush
struct __AtAutoreleasePool { __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();} ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);} void * atautoreleasepoolobj; }; -
objc_autoreleasePoolPush
objc_autoreleasePoolPush(void) { return AutoreleasePoolPage::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;
}
哨兵对象:

-
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 { // page == nil 的时候,子线程的时候 autoreleasepool 是nil,没有初始化,这句代码防止崩溃 return autoreleaseNoPage(obj); } } -
添加对象
id *add(id obj) { // 代码功能移动指针,并且返回插入的对象的指针地址 assert(!full()); unprotect(); id *ret = next; // faster than `return next-1` because of aliasing *next++ = obj; protect(); return ret; }
释放流程
-
objc_autoreleasePoolPop
objc_autoreleasePoolPop(void *ctxt) { AutoreleasePoolPage::pop(ctxt); } -
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); // pool boundary 地址 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(); //链表中的进行release 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
思考
思考1:
@autoreleasepool {
// insert code here...
id a = [[NSObject alloc] init];
NSLog(@"dsaf");
}
思考2:
@autoreleasepool {
// insert code here...
id __autoreleasing a = [[NSObject alloc] init];
NSLog(@"dsaf");
}
思考3:
@autoreleasepool {
// insert code here...
animal *a1 = [animal animal1];
@autoreleasepool {
animal *a2 = a1;
@autoreleasepool {
animal *a3 = a1;
}
}
}
思考4:
@autoreleasepool {
// insert code here...
animal *a1 = [animal animal1];
@autoreleasepool {
animal *a2 = [animal animal1];
@autoreleasepool {
animal *a3 = [animal animal1];
}
}
}
通过: _objc_autoreleasePoolPrint();
答案1:
objc[26458]: ##############
objc[26458]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[26458]: 1 releases pending.
objc[26458]: [0x103803000] ................ PAGE (hot) (cold)
objc[26458]: [0x103803038] ################ POOL 0x103803038
objc[26458]: ##############
答案2:
objc[26528]: ##############
objc[26528]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[26528]: 2 releases pending.
objc[26528]: [0x104803000] ................ PAGE (hot) (cold)
objc[26528]: [0x104803038] ################ POOL 0x104803038
objc[26528]: [0x104803040] 0x103306310 NSObject
objc[26528]: ##############
答案3:
objc[25612]: ##############
objc[25612]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[25612]: 2 releases pending.
objc[25612]: [0x102802000] ................ PAGE (hot) (cold)
objc[25612]: [0x102802038] ################ POOL 0x102802038
objc[25612]: [0x102802040] 0x100514d10 animal
objc[25612]: ##############
objc[25612]: ##############
objc[25612]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[25612]: 3 releases pending.
objc[25612]: [0x102802000] ................ PAGE (hot) (cold)
objc[25612]: [0x102802038] ################ POOL 0x102802038
objc[25612]: [0x102802040] 0x100514d10 animal
objc[25612]: [0x102802048] ################ POOL 0x102802048
objc[25612]: ##############
objc[25612]: ##############
objc[25612]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[25612]: 4 releases pending.
objc[25612]: [0x102802000] ................ PAGE (hot) (cold)
objc[25612]: [0x102802038] ################ POOL 0x102802038
objc[25612]: [0x102802040] 0x100514d10 animal
objc[25612]: [0x102802048] ################ POOL 0x102802048
objc[25612]: [0x102802050] ################ POOL 0x102802050
objc[25612]: ##############
思考4:
objc[21124]: ##############
objc[21124]: AUTORELEASE POOLS for thread 0x1000ad5c0
objc[21124]: 3 releases pending.
objc[21124]: [0x104802000] ................ PAGE (hot) (cold)
objc[21124]: [0x104802038] ################ POOL 0x104802038
objc[21124]: [0x104802040] 0x100593500 __NSCFString
objc[21124]: [0x104802048] 0x100593500 __NSCFString
objc[21124]: ##############
objc[21124]: ##############
objc[21124]: AUTORELEASE POOLS for thread 0x1000ad5c0
objc[21124]: 6 releases pending.
objc[21124]: [0x104802000] ................ PAGE (hot) (cold)
objc[21124]: [0x104802038] ################ POOL 0x104802038
objc[21124]: [0x104802040] 0x100593500 __NSCFString
objc[21124]: [0x104802048] 0x100593500 __NSCFString
objc[21124]: [0x104802050] ################ POOL 0x104802050
objc[21124]: [0x104802058] 0x100602a10 __NSCFString
objc[21124]: [0x104802060] 0x100602a10 __NSCFString
objc[21124]: ##############
objc[21124]: ##############
objc[21124]: AUTORELEASE POOLS for thread 0x1000ad5c0
objc[21124]: 8 releases pending.
objc[21124]: [0x104802000] ................ PAGE (hot) (cold)
objc[21124]: [0x104802038] ################ POOL 0x104802038
objc[21124]: [0x104802040] 0x100593500 __NSCFString
objc[21124]: [0x104802048] 0x100593500 __NSCFString
objc[21124]: [0x104802050] ################ POOL 0x104802050
objc[21124]: [0x104802058] 0x100602a10 __NSCFString
objc[21124]: [0x104802060] 0x100602a10 __NSCFString
objc[21124]: [0x104802068] ################ POOL 0x104802068
objc[21124]: [0x104802070] 0x103f000d0 NSTaggedPointerStringCStringContainer
objc[21124]: ##############
objc[21504]: ##############
objc[21504]: AUTORELEASE POOLS for thread 0x1000ad5c0
objc[21504]: 2 releases pending.
objc[21504]: [0x101802000] ................ PAGE (hot) (cold)
objc[21504]: [0x101802038] ################ POOL 0x101802038
objc[21504]: [0x101802040] 0x10079ef20 __NSCFString
objc[21504]: ##############
objc[21504]: ##############
objc[21504]: AUTORELEASE POOLS for thread 0x1000ad5c0
objc[21504]: 3 releases pending.
objc[21504]: [0x101802000] ................ PAGE (hot) (cold)
objc[21504]: [0x101802038] ################ POOL 0x101802038
objc[21504]: [0x101802040] 0x10079ef20 __NSCFString
objc[21504]: [0x101802048] ################ POOL 0x101802048
objc[21504]: ##############
objc[21504]: ##############
objc[21504]: AUTORELEASE POOLS for thread 0x1000ad5c0
objc[21504]: 4 releases pending.
objc[21504]: [0x101802000] ................ PAGE (hot) (cold)
objc[21504]: [0x101802038] ################ POOL 0x101802038
objc[21504]: [0x101802040] 0x10079ef20 __NSCFString
objc[21504]: [0x101802048] ################ POOL 0x101802048
objc[21504]: [0x101802050] ################ POOL 0x101802050
objc[21504]: ##############
这里加入的操作貌似系统做了相应的优化,不知道为何不同的写法,加入与否受系统控制。不过唯一肯定的是,当我们添加__autoreleasing 修饰的时候是肯定会添加到autoreleasepoolpage 当中的。