声明:本文仅供记忆使用,并不适合新手小白观看
autorealesepool 原理
- 自动释放池(即所有的AutoreleasePoolPage对象)是以栈为结点通过双向链表的形式组合而成;
- 自动释放池与线程一一对应;
- 每个
AutoreleasePoolPage对象占用4096字节内存,除了56字节用来存放它内部的成员变量,剩下4040字节的空间用来存放autorelease对象的地址 @autoreleasepool {}就相当于在代码前添加一个__AtAutoreleasePool结构体,所有局部变量通过push方法添加到AutoreleasePoolPage,若不够开辟新的AutoreleasePoolPage,作用域结束后通过pop方法释放对象- 调用
push方法会将一个POOL_BOUNDARY(哨兵对象)入栈,并且返回其存放的内存地址 - 调用pop方法时传入一个
POOL_BOUNDARY(哨兵对象)的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY(哨兵对象) next指向了下一个能存放autorelease对象地址的区域
autorealese相关问题
-
ARC 环境下,autorelease对象在什么时候释放?
-
手动干预释放,手动添加的@autoreleasepool{}
在当前作用域大括号结束时释放。
-
系统干预释放
有runloop,例如主线程或添加runloop的子线程在事件循环(RunLoop)的每次循环开始时,在当前线程创建一个自动释放池,并在每次循环结束时销毁它,在销毁时释放自动释放池中的所有autorelease对象。
iOS在主线程的Runloop中注册了2个Observer
第1个Observer --------
监听了kCFRunLoopEntry(即将进入Loop)事件,会调用objc_autoreleasePoolPush()
第2个Observer --------
监听了kCFRunLoopBeforeWaiting(即将进入休眠)事件,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
监听了kCFRunLoopBeforeExi(即将退出Loop)t事件,会调用objc_autoreleasePoolPop() -
系统干预释放
无runloop,例如没有添加runloop的子线程每一个线程创建的时候就会有一个
autoreleasepool的创建,并且在线程退出的时候,清空整个autoreleasepool。(ps:如果在子线程中设置一个循环,autorelease对象确实无法释放)
-
-
ARC环境下,需不需要手动添加@autoreleasepool?
如果我们需要在循环中创建了很多临时的
autorelease对象,则手动添加@autoreleasepool来管理这些对象可以很大程度地减少内存峰值。比如在for循环中alloc图片数据等内存消耗较大的场景,需要手动添加@autoreleasepool。 -
如果对 NSAutoreleasePool 对象调用 autorelease 方法会发生什么情况?
1
2NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool autorelease];抛出异常NSInvalidArgumentException并导致程序Crash,异常原因:不能对NSAutoreleasePool对象调用autorelease。
autorealesepool详细过程
-
autoreleasepool生成c++文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
1
2
3
4
5
6
7
8
9//原始OC代码
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
}
//c++代码
{
__AtAutoreleasePool __autoreleasepool;
NSObject *obj = [[NSObject alloc] init];
} -
一起就是在开始的时候调用 objc_autoreleasePoolPush()结束时候调用objc_autoreleasePoolPop(atautoreleasepoolobj)
1
2
3
4
5
6
7
8
9
10
11
12
13
14struct __AtAutoreleasePool {
//构造函数,在创建结构体的时候调用
__AtAutoreleasePool() {
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
// 写在autoreleasepool内的代码
NSObject *obj = [[NSObject alloc] init];
//析构函数,在结构体销毁的时候调用
~__AtAutoreleasePool(){
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
void * atautoreleasepoolobj;
}; -
具体源码可以再Runtime源码中查看,
push相关源码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24void * 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) {
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {//page没有满,就把obj对象加到page
return page->add(obj);
} else if (page) {//page满了 创建新的page
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
} -
具体源码可以再Runtime源码中查看,
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
55
56static 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 = 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 (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();
}
}
} -
类
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
25class AutoreleasePoolPage {
magic_t const magic;//用来校验 AutoreleasePoolPage 的结构是否完整
id *next;//指向最新添加的 autoreleased 对象的下一个位置,初始化时指向 begin() ;
pthread_t const thread;//指向当前线程;说明了,AutoreleasePoolPage和线程一一对应的。
AutoreleasePoolPage * const parent;//指向父结点
AutoreleasePoolPage *child;//指向子结点
uint32_t const depth;//depth 代表深度,从 0 开始,往后递增 1;
uint32_t hiwat;//hiwat 代表 high water mark
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);
}
...
}
参考文章
Runtime源码
探索子线程autorelease对象的释放时机
iOS - 聊聊 autorelease 和 @autoreleasepool