定义

AutoReleasePool是Objective—C中一种内存自动回收的机制,他可以将加入AutoReleasePool中的变量release的时机延迟。也就是说,当你创建一个对象,在正常情况下,变量会在超出其作用域的时候立即release,如果将该对象加入到自动释放池中,这个对象并不会立即释放,而是等到runloop休眠或者超出AutoReleasePool的作用域{}之后才会被释放。

本质:

  • autorelease本质就是延迟调用release方法
    • MRC环境下,通过[obj autorelease]来延迟内存的释放
    • ARC环境下,是不能手动调用,系统会自动给对象添加autorelease
  • 自动释放池的主要底层数据结构是:__AtAutoreleasePool、AutoreleasePoolPage
  • 调用了autorelease的对象最终都是通过AutoreleasePoolPage对象来管理的

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int main(int argc, char * argv[]) {
@autoreleasepool {
///ARC下会自动加入 autorelease方法
NSObject * obj = [[NSObject alloc] init];
///MRC写法
// NSObject * obj = [[[NSObject alloc] init] autorelease];
}
}

//c++代码,转译
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; ///调用了objc_autoreleasePoolPush
NSObject * obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
}
}

// 伪代码
int main (int argc, char * argv[]) {
// push
void *poolToken = objc_autoreleasePoolPush();

这中间为写在{...}中的代码

// pop 将{...}中的对象都执行一次 release操作
objc_autoreleasePoolPop(哨兵对象地址);//哨兵对象 POOL_BUNDARY
}

源码分析

AutoreleasePoolPage

  • 每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址
  • 所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class AutoreleasePoolPage 
{
//最大size 4096字节
PAGE_MAX_SIZE;
//用来校验AutoreleasePoolPage的结构是否完整
magic_t const magic;
//指向下一个即将产生的autoreleased对象的存放位置(当next == begin()时,表示AutoreleasePoolPage为空;当next == end()时,表示AutoreleasePoolPage已满
id *next;
//指向当前线程,一个AutoreleasePoolPage只会对应一个线程,但一个线程可以对应多个AutoreleasePoolPage;
pthread_t const thread;
//指向父结点,第一个结点的 parent 值为 nil;
AutoreleasePoolPage * const parent;
//指向子结点,最后一个结点的 child 值为 nil;
AutoreleasePoolPage *child;
//代表深度,第一个page的depth为0,往后每递增一个page,depth会加1;
uint32_t const depth;
}

NSObject.mm - push

objc_autoreleasePoolPush

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void *
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;
}

static inline id *autoreleaseFast(id obj)
{
//hotPage()表示当前页的 AutoreleasePoolPage 节点
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
// 当前 page 存在且没有满时,直接将对象添加到当前 page 中,即 next 指向的位置
return page->add(obj);
} else if (page) {
// 当前 page 存在且已满时,创建一个新的 page ,并将对象添加到新创建的 page 中
return autoreleaseFullPage(obj, page);
} else {
// 当前 page 不存在时,即还没有 page 时,创建第一个 page ,并将对象添加到新创建的 page 中
return autoreleaseNoPage(obj);
}
}

NSObject.mm - pop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
void objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}

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
page = pageForPointer(token);
stop = (id *)token;

if (PrintPoolHiwat) printHiwat();

// 从栈顶开始操作出栈,并向栈中的对象发送release消息,直到遇到第一个哨兵对象(POOL_BUNDARY标记)
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();
}
}
}

autoreleasePool的结构和工作原理

  • autoreleasepool本质上就是一个指针堆栈,内部结构是由若干个以AutoreleasePoolPage对象为结点的双向链表组成,系统会在需要的时候动态地增加或删除page节点。

push,pop大体流程

  • 调用push方法会将一个POOL_BOUNDARY入栈,并且返回其存放的内存地址
  • 调用pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY
  • id *next指向了下一个能存放autorelease对象地址的区域

Runloop和Autorelease

  • iOS在主线程的Runloop中注册了2个Observer
    • 第1个Observer监听了kCFRunLoopEntry事件,会调用objc_autoreleasePoolPush()
    • 第2个Observer
      • 监听了kCFRunLoopBeforeWaiting事件,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
      • 监听了kCFRunLoopBeforeExit事件,会调用objc_autoreleasePoolPop()

参考