一、自动释放池(AutoreleasePoolPage)
每个工程都有一个main
函数入口,通常是这样的:
int main(int argc, const char *argv[]){
@autoreleasepool{
// other code
}
return 0;
}
这里有一个@autoreleasepool
,无法一眼看出代表的是什么,可以通过rewrite -objc
命令来重写main.m
文件,改写后的main
函数变成这样的:
int main(int argc, const char *argv[]){
/* @autoreleasepool */ {__AtAutoreleasePool __autoreleasepool;
// other code
}
}
可以看出,@autoreleasepool
被编译器转为了__AtAutoreleasePool __autoreleasepool
。__AtAutoreleasePool
的定义可以在该文件中找到:
struct __AtAutoreleasePool{
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void *atautoreleasepoolobj;
}
__AtAutoreleasePool
结构体封装了一个对象atautoreleasepoolobj
,构造函数时使用objc_autoreleasePoolPush
进行赋值,析构函数调用时使用objc_autoreleasePoolPop
进行释放。
void *objc_autoreleasePoolPush(void){
return AutoreleasePoolPage:push();
}
void objc_autoreleasePoolPop(void *ctxt){
AutoreleasePoolPage:pop(ctxt);
}
这两个函数是对AutoreleasePoolPage
的push
和pop
操作进行了一次封装。AutoreleasePoolPage
就是我们今天的主角——自动释放池。
二、AutoreleasePoolPage的实现
1. AutoreleasePoolPage结构
class AutoreleasePoolPage{
magic_t const magic; // 用于校验的字段
id *next; // 当前存储自动释放对象的指针
pthread_t const thread; // 当前释放池所在的线程
AutoreleasePoolPage *const parent; // 前一个节点
AutoreleasePoolPage *child; // 下一个节点
uint32_t const depth; // 深度
uint32_t hitwat;
}
AutoreleasePoolPage
是使用双向链表实现的,如下图:
2.push的实现
#define POOL_BOUNDARY nil
static inline void *push{
return autoreleaseFast(POOL_BOUNDARY);
}
static inline id *autoreleaseFast(id obj){
AutoreleasePoolPage *page = hotPage();
if(page && !page->full()){
return page->add(obj);
}else if(page){
return autoreleseFullPage(obj, page);
}else{
return autoreleaseNoPage(obj);
}
}
push
主要通过autoreleaseFast
来返回一个可用的page
:
1. hotPage是指当前页,如果当前页没满的话,那将添加到当前页;
2. 当前页已满,通过autoreleseFullPage生成一个当前page的下一个链表节点,并添加上去;
3. 当前页为空,生成第一个链表节点,添加进去。
3.pop的实现
static inline void pop(void *token){
AutoreleasePoolPage *page = pageForPointer(token); // 通过指针位移可以获取到token所载的poolPage
id *stop = (id*)token;
page->releaseUntil(stop);
if(page->child){
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 = page->parent;
setHotPage(page);
}
page->unprotect();
id obj = *--page->next;
memset((void *)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
object_release(obj); // 对obj执行release操作
}
setHotPage(this);
}
在main
函数中的临时变量__autoreleasepool
,在执行完之后,析构函数会将执行AutoreleasePoolPage:pop(__autoreleasepool)
,将该变量之后加入自动释放池的所有对象都进行一次release
操作,从而达到自动释放的效果。
4.autorelease
static int id autorelease(id obj){
return autoreleaseFast(obj);
}
autorelease
实现跟add
一样,将对象加入到自动释放池中。由于ARC下编译器自动帮你确定了调用autorelease
的时机,所以不需要显试调用,当然也无法显试调用。
小结
-
@autoreleasepool
其实就是AutoreleasePoolPage
的方便使用入口。 - autoreleasepool块执行结束后,会执行pop操作,将这期间所有的autorelease对象释放。
网友评论