博客连接AutoreleasePool的实现编程
在MRC中,调用[obj autorelease]
来延迟内存的释放;在ARC下,对象调用autorelease
方法,就会被自动添加到最近的自动释放池,只有当自动释放池被销毁的时候,才会执行release
方法,进行释放。真实结果究竟是什么,等看完源码后咱们就会知道了。缓存
main.m
中有一段代码:bash
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
复制代码
转换成C++代码:app
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
复制代码
@autoreleasepool
变成__AtAutoreleasePool __autoreleasepool
。__AtAutoreleasePool
结构体定义以下:less
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
复制代码
它提供了两个方法:objc_autoreleasePoolPush()
和objc_autoreleasePoolPop
。这两个方法的定义在NSObject.mm
文件中,分别是:函数
void *
objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt) {
AutoreleasePoolPage::pop(ctxt);
}
复制代码
因此,autoreleasepool的自动释放的核心就是AutoreleasePoolPage类。oop
在NSObject.mm
文件中定义了AutoreleasePoolPage
,这里咱们只显示这个类比较重要的属性,以下:测试
class AutoreleasePoolPage {
static size_t const SIZE = PAGE_MAX_SIZE;//SIZE是AutoreleasePoolPage的大小,4096个字节
magic_t const magic; //autoreleasepool完整性校验
id *next;//AutoreleasePoolPage单个节点是一个链表,next指向栈顶的最新的autorelease对象的下一个位置
pthread_t const thread;//当前所在的线程
AutoreleasePoolPage * const parent;//指针
AutoreleasePoolPage *child;//指针
uint32_t const depth;//深度
uint32_t hiwat;
}
复制代码
经过源码咱们能够知道:优化
AutoreleasePoolPage
为节点的双向链表。AutoreleasePoolPage
节点是一个堆栈结,且大小为4096个字节。AutoreleasePoolPage
节点对应着一个线程,属于一一对应关系。AutoreleasePool结构如图所示:ui
接着咱们看一下AutoreleasePoolPage
的构造函数以及一些操做方法:
//构造函数
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();
}
//相关操做方法
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;
}
复制代码
AutoreleasePoolPage
节点开始存autorelease对象的位置。AutoreleasePoolPage
节点最大的位置next
指向beigin()说明为空next
指向end)说明满了因此一个空的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;
}
复制代码
push执行的时候首先会进行判断,若是是须要每一个pool都生成一个新page,即DebugPoolAllocation
为YES
,则执行autoreleaseNewPage
方法,不然执行autoreleaseFast
方法。
autoreleaseNewPage
分为两种状况:
autoreleaseFullPage
方法;autoreleaseNoPage
方法。autoreleaseFast
分为三种状况:
add()
方法进行添加;autoreleaseFullPage
方法;autoreleaseNoPage
方法。前面讲到的page其实就是hotPage
,经过AutoreleasePoolPage *page = 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;
}
复制代码
经过上面的代码咱们知道当前页是存在TLS(线程私有数据)
里面的。因此说第一次调用push的时候,没有page天然连hotPage也没有。
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
节点,直到查找到一个未满的AutoreleasePoolPage
。接着使用AutoreleasePoolPage
构造函数传入parent
建立一个新的AutoreleasePoolPage
的节点(此时跳出了while循环)。
在查找到一个可使用的AutoreleasePoolPage
以后,会将该页面标记成hotPage
,而后调动add()
方法添加对象。
static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
//"no page"意味着没有没有池子被push或者说push了一个空的池子
assert(!hotPage());
bool pushExtraBoundary = false;
if (haveEmptyPoolPlaceholder()) {//push了一个空的池子
pushExtraBoundary = true;
}
else if (obj != POOL_BOUNDARY && DebugMissingPools) {
_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) {
//没有池子被push
return setEmptyPoolPlaceholder();
}
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
if (pushExtraBoundary) {
//push了一个空的池子,添加哨兵对象
page->add(POOL_BOUNDARY);
}
return page->add(obj);
}
//haveEmptyPoolPlaceholder的本质
static inline bool haveEmptyPoolPlaceholder()
{
id *tls = (id *)tls_get_direct(key);
return (tls == EMPTY_POOL_PLACEHOLDER);
}
复制代码
从上面的代码咱们能够知道,既然当前内存中不存在AutoreleasePoolPage
,就要从头开始构建这个自动释放池的双向链表,也就是说,新的AutoreleasePoolPage
是没有parent
指针的。
初始化以后,将当前页标记为hotPage
,而后会先向这个page
中添加一个POOL_BOUNDARY
的标记,来确保在pop
调用的时候,不会出现异常。
最后,将obj
添加到自动释放池中。
接着看一下当对象调用autorelase
方法发生了什么。
- (id)autorelease {
return ((id)self)->rootAutorelease();
}
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;
}
复制代码
从上面的源码咱们看到,对象调用autorelase
方法,最后会变成AutoreleasePoolPage
的autorelease
函数。AutoreleasePoolPage
的autorelease
的本质就是调用autoreleaseFast(obj)
函数。只不过push
操做插入的是一个POOL_BOUNDARY
,而autorelease
操做插入的是一个具体的autoreleased
对象即AutoreleasePoolPage
入栈操做。
固然这么说并不严谨,由于咱们须要考虑是不是Tagged Pointer
和是否进行优化的状况(prepareOptimizedReturn
这个后面也会提到),若是不知足这两个条件才会进入缓存池。
因此push的流程是:
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
//第一种状况:autoreleasepool首次push的时候返回的,也就是最顶层的page执行pop会执行这一部分
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);
//https://stackoverflow.com/questions/24952549/does-nsthread-create-autoreleasepool-automatically-now
//第二种状况:在非ARC的状况下,在新建立的线程中不使用autoreleasepool,直接调用autorelease方法时会出现这个状况。此时没有pool,直接进行autorelease。
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) {
while (this->next != stop) {
AutoreleasePoolPage *page = hotPage();
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);
}
复制代码
从next指针开始,一个一个向前调用objc_release
,直到碰到push时压入的pool为止。
因此autoreleasePool的运行过程应该是:
pool1 = push()
...
pool2 = push()
...
pool3 = push()
...
pop(pool3)
...
pop(pool2)
...
pop(pool1)
复制代码
每次pop,实际上都会把最近一次push以后添加进去的对象所有release掉。
苹果的文档中提到:
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开始以前会自动建立一个autoreleasePool,并在结束时pop。那其余的线程呢?这里我作了一个实验,代码以下:
每个线程都会维护本身的autoreleasePool堆栈,也就是说每个autoreleasePool对应一个线程。
那什么样的对象会进入autoreleasePool呢?
测试1.1代码:
NSMutableArray *arr = [NSMutableArray new];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
复制代码
结果
2019-01-22 15:50:45.129263+0800 AutoreleasePool[31529:22121744] 1
objc[31529]: ##############
objc[31529]: AUTORELEASE POOLS for thread 0x1176345c0
objc[31529]: 2 releases pending.
objc[31529]: [0x7f96e0802000] ................ PAGE (hot) (cold)
objc[31529]: [0x7f96e0802038] 0x600003ac8f00 __NSArrayI
objc[31529]: [0x7f96e0802040] 0x600000ceef80 __NSSetI
objc[31529]: ##############
复制代码
测试1.2代码:
@autoreleasepool{
NSMutableArray *arr = [NSMutableArray new];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
}
复制代码
结果
2019-01-22 15:53:28.818873+0800 AutoreleasePool[31568:22134125] 1
objc[31568]: ##############
objc[31568]: AUTORELEASE POOLS for thread 0x10d20e5c0
objc[31568]: 3 releases pending.
objc[31568]: [0x7fcf66002000] ................ PAGE (hot) (cold)
objc[31568]: [0x7fcf66002038] 0x600000129200 __NSArrayI
objc[31568]: [0x7fcf66002040] 0x60000370b020 __NSSetI
objc[31568]: [0x7fcf66002048] ################ POOL 0x7fcf66002048
objc[31568]: ##############
复制代码
测试1.3代码:
{
NSMutableArray *arr = [NSMutableArray new];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
}
@autoreleasepool{
NSMutableArray *arr = [NSMutableArray new];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
}
复制代码
结果
2019-01-22 15:55:21.271452+0800 AutoreleasePool[31596:22141965] 1
objc[31596]: ##############
objc[31596]: AUTORELEASE POOLS for thread 0x1166f15c0
objc[31596]: 2 releases pending.
objc[31596]: [0x7fdcaf002000] ................ PAGE (hot) (cold)
objc[31596]: [0x7fdcaf002038] 0x600003e6a500 __NSArrayI
objc[31596]: [0x7fdcaf002040] 0x600000849db0 __NSSetI
objc[31596]: ##############
2019-01-22 15:55:21.272353+0800 AutoreleasePool[31596:22141965] 1
objc[31596]: ##############
objc[31596]: AUTORELEASE POOLS for thread 0x1166f15c0
objc[31596]: 3 releases pending.
objc[31596]: [0x7fdcaf002000] ................ PAGE (hot) (cold)
objc[31596]: [0x7fdcaf002038] 0x600003e6a500 __NSArrayI
objc[31596]: [0x7fdcaf002040] 0x600000849db0 __NSSetI
objc[31596]: [0x7fdcaf002048] ################ POOL 0x7fdcaf002048
objc[31596]: ##############
复制代码
测试2.1代码:
NSMutableArray *arr = [NSMutableArray array];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
复制代码
结果
2019-01-22 15:57:02.360860+0800 AutoreleasePool[31615:22149043] 2
objc[31615]: ##############
objc[31615]: AUTORELEASE POOLS for thread 0x1111eb5c0
objc[31615]: 3 releases pending.
objc[31615]: [0x7fbf00002000] ................ PAGE (hot) (cold)
objc[31615]: [0x7fbf00002038] 0x600003f15c00 __NSArrayI
objc[31615]: [0x7fbf00002040] 0x6000009705f0 __NSSetI
objc[31615]: [0x7fbf00002048] 0x600002404f30 __NSArrayM
objc[31615]: ##############
复制代码
测试2.2代码:
@autoreleasepool {
NSMutableArray *arr = [NSMutableArray array];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
}
复制代码
结果
2019-01-22 15:58:29.932693+0800 AutoreleasePool[31634:22153810] 2
objc[31634]: ##############
objc[31634]: AUTORELEASE POOLS for thread 0x115aac5c0
objc[31634]: 4 releases pending.
objc[31634]: [0x7f867c002000] ................ PAGE (hot) (cold)
objc[31634]: [0x7f867c002038] 0x600000b00080 __NSArrayI
objc[31634]: [0x7f867c002040] 0x600003d64190 __NSSetI
objc[31634]: [0x7f867c002048] ################ POOL 0x7f867c002048
objc[31634]: [0x7f867c002050] 0x60000100ff30 __NSArrayM
objc[31634]: ##############
复制代码
测试2.3代码:
{
NSMutableArray *arr = [NSMutableArray array];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
}
@autoreleasepool {
NSMutableArray *arr = [NSMutableArray array];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
}
复制代码
结果
2019-01-22 16:01:11.925690+0800 AutoreleasePool[31670:22164284] 2
objc[31670]: ##############
objc[31670]: AUTORELEASE POOLS for thread 0x11bffa5c0
objc[31670]: 3 releases pending.
objc[31670]: [0x7ff965802000] ................ PAGE (hot) (cold)
objc[31670]: [0x7ff965802038] 0x600001c1eb00 __NSArrayI
objc[31670]: [0x7ff965802040] 0x600002a47200 __NSSetI
objc[31670]: [0x7ff965802048] 0x600000712490 __NSArrayM
objc[31670]: ##############
2019-01-22 16:01:11.926577+0800 AutoreleasePool[31670:22164284] 1
objc[31670]: ##############
objc[31670]: AUTORELEASE POOLS for thread 0x11bffa5c0
objc[31670]: 4 releases pending.
objc[31670]: [0x7ff965802000] ................ PAGE (hot) (cold)
objc[31670]: [0x7ff965802038] 0x600001c1eb00 __NSArrayI
objc[31670]: [0x7ff965802040] 0x600002a47200 __NSSetI
objc[31670]: [0x7ff965802048] 0x600000712490 __NSArrayM
objc[31670]: [0x7ff965802050] ################ POOL 0x7ff965802050
objc[31670]: ##############****
复制代码
从上面的代码咱们能够知道,使用new
、alloc
这样的方法建立的对象实例是不会进入autoreleasePool的,可是使用简便方法建立的对象如[NSMutableArray array]
是会进入自动缓存池的。
可是在测试2.3上,咱们能够看到,只有一个array进入了自动缓存池,另一个没有进入。看一下它的方法调用栈:
在《Objective-C高级编程》第66-67页提到了最优化程序运行。经过objc_retainAutoreleasedReturnValue
和objc_retainAutoreleaseReturnValue
函数的协做,能够不将对象注册到autoreleasePool中而直接传递,这一过程达到最优化。
objc_retainAutoreleasedReturnValue
的实现以下:
// Accept a value returned through a +0 autoreleasing convention for use at +1.
id
objc_retainAutoreleasedReturnValue(id obj)
{
if (acceptOptimizedReturn() == ReturnAtPlus1) return obj;
return objc_retain(obj);
}
// Try to accept an optimized return.
// Returns the disposition of the returned object (+0 or +1).
// An un-optimized return is +0.
static ALWAYS_INLINE ReturnDisposition
acceptOptimizedReturn()
{
ReturnDisposition disposition = getReturnDisposition();
setReturnDisposition(ReturnAtPlus0); // reset to the unoptimized state
return disposition;
}
static ALWAYS_INLINE ReturnDisposition
getReturnDisposition()
{
return (ReturnDisposition)(uintptr_t)tls_get_direct(RETURN_DISPOSITION_KEY);
}
复制代码
经过上面的代码咱们能够知道objc_retainAutoreleasedReturnValue
会尝试接收一个被优化的结果,如何是ReturnAtPlus1
即YES
,返回对象自己,不然执行objc_retain(obj
。
这个被优化的结果是在线程私有数据TLS中的,咱们能够理解为一个优化位。当优化位返回YES的时候,直接返回对象自己,不然执行retain。
objc_retainAutoreleaseReturnValue
的实现以下:
// Prepare a value at +0 for return through a +0 autoreleasing convention.
id
objc_retainAutoreleaseReturnValue(id obj)
{
if (prepareOptimizedReturn(ReturnAtPlus0)) return obj;
// not objc_autoreleaseReturnValue(objc_retain(obj))
// because we do not need another optimization attempt
return objc_retainAutoreleaseAndReturn(obj);
}
// Try to prepare for optimized return with the given disposition (+0 or +1).
// Returns true if the optimized path is successful.
// Otherwise the return value must be retained and/or autoreleased as usual.
static ALWAYS_INLINE bool
prepareOptimizedReturn(ReturnDisposition disposition)
{
assert(getReturnDisposition() == ReturnAtPlus0);
if (callerAcceptsOptimizedReturn(__builtin_return_address(0))) {
if (disposition) setReturnDisposition(disposition);
return true;
}
return false;
}
static ALWAYS_INLINE bool
callerAcceptsOptimizedReturn(const void * const ra0)
{
const uint8_t *ra1 = (const uint8_t *)ra0;
const unaligned_uint16_t *ra2;
const unaligned_uint32_t *ra4 = (const unaligned_uint32_t *)ra1;
const void **sym;
#define PREFER_GOTPCREL 0
#if PREFER_GOTPCREL
// 48 89 c7 movq %rax,%rdi
// ff 15 callq *symbol@GOTPCREL(%rip)
if (*ra4 != 0xffc78948) {
return false;
}
if (ra1[4] != 0x15) {
return false;
}
ra1 += 3;
#else
// 48 89 c7 movq %rax,%rdi
// e8 callq symbol
if (*ra4 != 0xe8c78948) {
return false;
}
ra1 += (long)*(const unaligned_int32_t *)(ra1 + 4) + 8l;
ra2 = (const unaligned_uint16_t *)ra1;
// ff 25 jmpq *symbol@DYLDMAGIC(%rip)
if (*ra2 != 0x25ff) {
return false;
}
#endif
ra1 += 6l + (long)*(const unaligned_int32_t *)(ra1 + 2);
sym = (const void **)ra1;
if (*sym != objc_retainAutoreleasedReturnValue &&
*sym != objc_unsafeClaimAutoreleasedReturnValue)
{
return false;
}
return true;
}
// Same as objc_retainAutorelease but suitable for tail-calling
// if you do not want to push a frame before this point.
__attribute__((noinline))
static id
objc_retainAutoreleaseAndReturn(id obj)
{
return objc_retainAutorelease(obj);
}
id
objc_retainAutorelease(id obj)
{
return objc_autorelease(objc_retain(obj));
}
复制代码
这里主要涉及到callerAcceptsOptimizedReturn
,这个函数意思不是很理解,可是里面涉及到了objc_retainAutoreleasedReturnValue
,猜想多是程序检测在返回值以后是否紧接着调用了objc_retainAutoreleasedReturnValue
,若是是,就知道了外部是ARC环境走优化路线,反之就走没被优化的逻辑。
因此我的认为,使用new
、alloc
这样的方法建立的对象实例是不会进入autoreleasePool的,可是使用简便方法建立的对象,程序会进行优化后,再决定是否进入自动缓存池。
另外关于main函数中的@autoreleasepool
的做用是什么?简单的说就是让那些进入自动缓存池的对象有个地方被释放。
在APP中,整个主线程是运行在一个自动释放池中的。
main函数中的自动释放池的做用:这个池块给出了一个pop点来显式的告诉咱们这里有一个释放点,若是你的main在初始化的过程当中有别的内容能够放在这里。
使用@autoreleasepool
标记,调用push()方法。
没有hotpage,调用autoreleaseNoPage()
,设置EMPTY_POOL_PLACEHOLDER
。
由于设置了EMPTY_POOL_PLACEHOLDER
,因此会设置本页为hotpage
,添加边界标记POOL_BOUNDARY
,最后添加obj。
继续有对象调用autorelease
,此时已经有了page,调用page->add(obj)
。
若是page满了,调用autoreleaseFullPage()
建立新page,重复第6点。
到达autoreleasePool边界,调用pop方法,一般状况下会释放掉POOL_BOUNDARY
以后的全部对象