AutoreleasePool 是一个抽象概念,并没有实际结构,真实的结构是一个双向链表『AutoreleasePoolPage』,由C++实现。
1.数据结构
其数据结构如下:
class AutoreleasePoolPage
{
#define POOL_SENTINEL 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;
}
成员变量说明:
next
:游标,一直指向最新入栈的autorelease对象的下一个位置。
key
:TLS技术,既Thread Local Storage(TLS)线程局部存储。目的很简单,将一块内存作为某个线程专有的存储,以key-value的形式进行读写。AutoreleasePoolPage将这块区域用作存储最新的Page,既hotPage。
SIZE
:单个page的最大存储数量,AutoreleasePoolPage以双向链表的形式存在,但是单个page的存储是有限额的,(id *) ((uint8_t *)this+SIZE)
。
thread
:当前线程pthread_self()
。
2.push和pop
当我们手动调用@autoreleasePool的时候,编译器会自动将大括号内的所有对象标记为autorelease前缀。AutoreleasePoolPage::autorelease((id)this)
当前对象本身作为参数入参,不过在讨论对象的autorelease之前,编译器还插入了两个方法:
void *pool = objc_autoreleasePoolPush();
……
……
……
objc_autoreleasePoolPop(pool);
前者做了和对象的autorelease相同的事情:
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);
}
}
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(pool);
的入参中,pool记录着一个pool的初始位置,根据这个位置和当前的hotPage位置,遍历中间所有的对象,进行释放。并且由于双向链表的结构,很容易跨page进行遍历。
网友评论