临时变量什么时候释放

自动释放池原理: AutoreleasePoolPage
Autorelease pool implementation
A thread's autorelease pool is a stack of pointers.
Each pointer is either an object to release, or POOL_BOUNDARY which is
an autorelease pool boundary.
A pool token is a pointer to the POOL_BOUNDARY for that pool. When
the pool is popped, every object hotter than the sentinel is released.
The stack is divided into a doubly-linked list of pages. Pages are added
and deleted as necessary.
Thread-local storage points to the hot page, where newly autoreleased
objects are stored.
1.先进后出 2. POOL_BOUNDARY 边界对象,用来区分AutoreleasePoolPage自己的成员变量和加进去的对象

自动释放池的结构属性 AutoreleasePoolPageData
struct AutoreleasePoolPageData
{
magic_t const magic; // 16 用来校验 AutoreleasePoolPage 的结构是否完整
__unsafe_unretained id *next; //8指向最新添加的 autoreleased 对象的下一个位置,初始化时指向begin() ;
pthread_t const thread; // 8 指向当前线程
AutoreleasePoolPage * const parent; //8 指向父结点,第一个结点的 parent 值为 nil ;
AutoreleasePoolPage *child; //8 指向子结点,最后一个结点的 child 值为 nil ;
uint32_t const depth; // 4 代表深度,从 0 开始,往后递增 1;
uint32_t hiwat; // 4 代表 high water mark 最大入栈数量标记
AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
: magic(), next(_next), thread(_thread),
parent(_parent), child(nil),
depth(_depth), hiwat(_hiwat)
{
}
};
打印autoreleasePool,
发现自动释放池是一页一页的,每页容量505个对象,第一页会自动存放一个pool对象,就是边界。一页满了,会标记page(full),否则为page(hot),第一页(cold)标记

objc_autoreleasePoolPush
找到hotPage,没有就创建,有就添加,满就从新创建
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);
}
}
add
id *add(id obj)
{
ASSERT(!full());
unprotect();
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;
protect();
return ret;
}
autoreleaseFullPage
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);
}
autoreleaseNoPage
id *autoreleaseNoPage(id obj)
{
// 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);
}
示意图

objc_autoreleasePoolPop
pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
// Popping the top-level placeholder pool.
page = hotPage();
if (!page) {
// Pool was never used. Clear the placeholder.
return setHotPage(nil);
}
// Pool was used. Pop its contents normally.
// Pool pages remain allocated for re-use as usual.
page = coldPage();
token = page->begin();
} else {
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 (slowpath(PrintPoolHiwat || DebugPoolAllocation || DebugMissingPools)) {
return popPageDebug(token, page, stop);
}
return popPage<false>(token, page, stop);
}
popPage
释放表和对象
static void
popPage(void *token, AutoreleasePoolPage *page, id *stop)
{
if (allowDebug && PrintPoolHiwat) printHiwat();
page->releaseUntil(stop);
// memory: delete empty children
if (allowDebug && DebugPoolAllocation && page->empty()) {
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (allowDebug && 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();
}
}
}
循环释放对象
从page页的最后一个一个类似递归往回释放objc_release(obj) page->empty(),next指针指向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
}
@autoreleasepool 嵌套
嵌套 - 只会创建一个page - 但是有两个边界对象,表示一个autoreleasepool,的大小,所以边界对象的作用是重复利用这个表,表示2个作用域空间
2020-03-22 16:50:59.630491+0800 KCObjcTest[90615:10937555] objc = <NSObject: 0x101603e90>
objc[90615]: ##############
objc[90615]: AUTORELEASE POOLS for thread 0x1000d2dc0
objc[90615]: 2 releases pending.
objc[90615]: [0x104002000] ................ PAGE (hot) (cold)
objc[90615]: [0x104002038] ################ POOL 0x104002038
objc[90615]: [0x104002040] 0x101603e90 NSObject
objc[90615]: ##############
2020-03-22 16:50:59.631795+0800 KCObjcTest[90615:10937874] obj = <NSObject: 0x101208aa0>
objc[90615]: ##############
objc[90615]: AUTORELEASE POOLS for thread 0x7000000cb000
objc[90615]: 3 releases pending.
objc[90615]: [0x104800000] ................ PAGE (hot) (cold)
objc[90615]: [0x104800038] ################ POOL 0x104800038
objc[90615]: [0x104800040] ################ POOL 0x104800040
objc[90615]: [0x104800048] 0x101208aa0 NSObject