@autoreleasepool
是自动释放池,用来进行内存管理,最常应用在循环中。平常在main
函数也会见到:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
@autoreleasepool
在ARC
或者MRC
下都能使用,其实@autoreleasepool
就是代替了NSAutoreleasePool
,NSAutoreleasePool
只能在MRC
下使用:
NSAutoreleasePool *pool = [NSAutoreleasePool alloc] init];//相当于objc_autoreleasePoolPush()
[pool drain];//相当于objc_autoreleasePoolPop(atautoreleasepoolobj)
- 用
clang
把它编译成cpp
文件看一下@autoreleasepool
转换成了__AtAutoreleasePool
:
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_g0_r4439vk96pq6tsnp9m9njklm0000gn_T_main_dc0893_mi_0);
}
return 0;
}
//自动释放池
struct __AtAutoreleasePool {
__AtAutoreleasePool() {
atautoreleasepoolobj = objc_autoreleasePoolPush();//构造函数
}
~__AtAutoreleasePool() {
objc_autoreleasePoolPop(atautoreleasepoolobj);//析构函数
}
void * atautoreleasepoolobj;
};
- 从objc4-750源码探索一下,先来看构造函数,使用
@autoreleasepool
时便会调用构造函数:
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();//AutoreleasePool由AutoreleasePoolPage组成
}
class AutoreleasePoolPage
{
...
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);//插入操作;POOL_BOUNDARY:哨兵对象,标识,边界
}
assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
...
}
- 如果没有初始化过就调用
autoreleaseNewPage
,最终会调用page->add
:
class AutoreleasePoolPage
{
...
static inline void setHotPage(AutoreleasePoolPage *page)
{
if (page) page->fastcheck();
tls_set_direct(key, (void *)page);//保存
}
...
static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
...
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);//初始化
setHotPage(page);//保存到TLS
if (pushExtraBoundary) {
page->add(POOL_BOUNDARY);//添加标识
}
return page->add(obj);//添加
}
...
static __attribute__((noinline))
id *autoreleaseNewPage(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page) return autoreleaseFullPage(obj, page);
else return autoreleaseNoPage(obj);//还没创建,调用这个方法
}
...
}
- 回到第3步,如果初始化过,就会调用
autoreleaseFast
。或者在@autoreleasepool
作用域里创建对象时,会自动调用autorelease
,最终也会调用autoreleaseFast
:
@implementation NSObject
...
- (id)autorelease {
return ((id)self)->rootAutorelease();
}
inline id
objc_object::rootAutorelease()
{
if (isTaggedPointer()) return (id)this;
if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
return rootAutorelease2();
}
__attribute__((noinline,used))
id
objc_object::rootAutorelease2()
{
assert(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
class AutoreleasePoolPage
{
...
static inline id autorelease(id obj)
{
assert(obj);
assert(!obj->isTaggedPointer());
id *dest __unused = autoreleaseFast(obj);//插入操作
assert(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}
...
}
class AutoreleasePoolPage
{
...
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);//没有就新建
}
}
...
}
- 首先会通过
key
取出AutoreleasePoolPage
返回:
class AutoreleasePoolPage
{
...
static inline AutoreleasePoolPage *hotPage()
{
AutoreleasePoolPage *result = (AutoreleasePoolPage *)
tls_get_direct(key);//当前线程的局部私有空间取出来的,TLS,私有数据,只有当前线程能访问到
if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
if (result) result->fastcheck();
return result;
}
...
}
- 然后分情况进行
add
。
如果AutoreleasePoolPage
存在没有满,就直接add
;
如果AutoreleasePoolPage
不存在,就调用autoreleaseNoPage
初始化,然后再add
(详细参考上面);
如果AutoreleasePoolPage
存在但满了,就调用autoreleaseFullPage
再创建一个,然后再add
:
class AutoreleasePoolPage
{
...
static __attribute__((noinline))
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
do {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);//不够,再创建一个新表
} while (page->full());
setHotPage(page);//保存
return page->add(obj);//添加
}
-
AutoreleasePoolPage
有大小限制,大小是4096
字节,一张表不够用就会创建另外一张,以双向链表连接起来形成一个自动释放池:

- 接着就来看看
add
操作,一开始添加的是传入的POOL_BOUNDARY
,之后的才是添加需要自动释放的对象。POOL_BOUNDARY
哨兵对象的作用是作为标识,用来区别每个AutoreleasePoolPage
边界:
class AutoreleasePoolPage
{
...
id *add(id obj)
{
assert(!full());
unprotect();
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;//添加后开始地址自然要变
protect();
return ret;
}
...
}
- 然后来看析构函数,出释放池作用域时会调用:
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
class AutoreleasePoolPage
{
...
static inline void pop(void *token)
{
...
page = pageForPointer(token);//取出
...
page->releaseUntil(stop);//链表当中的对象进行release操作
...
}
...
}
- 先取出
AutoreleasePoolPage
:
class AutoreleasePoolPage
{
...
static AutoreleasePoolPage *pageForPointer(uintptr_t p)
{
AutoreleasePoolPage *result;
uintptr_t offset = p % SIZE;//偏移量
assert(offset >= sizeof(AutoreleasePoolPage));
result = (AutoreleasePoolPage *)(p - offset);
result->fastcheck();
return result;
}
...
}
- 然后进行释放:
class AutoreleasePoolPage
{
...
void releaseUntil(id *stop)
{
while (this->next != stop) {//POOL_BOUNDARY
// Restart from hotPage() every time, in case -release
// autoreleased more objects
AutoreleasePoolPage *page = hotPage();
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);//释放
}
}
...
}
...
}
void
objc_release(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
return obj->release();
}
每调用一次
push
就会创建autoreleasepool
,然后往AutoreleasePoolPage
中的next
位置插入一个POOL_BOUNDARY
,并返回插入的POOL_BOUNDARY
内存地址。之后便是把需要释放的对象添加到next
指向的位置。当pop
时,把POOL_BOUNDARY
之后的所有autoreleased
对象释放。
-
线程、Runloop、释放池三者关系
-
每个线程都有对应的
Runloop
,并且会在有需要的时候自动创建。 -
每一个自动释放池对应一个线程,但一个线程可能有多个自动释放池。自动释放池会保存到线程的局部储存空间中,每个线程都会维护自己的自动释放池。线程结束时,会进行释放。

- 在当前线程的每个
event loop
开始前,系统会自动创建一个autoreleasepool
,并在event loop
结束时释放。
比如常驻线程下,使用定时器:
- (void)timer {
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(test) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];
}
- (void)test {
//__autoreleasing,在ARC中会自动注册到autoreleasepool中
__autoreleasing id test = [NSObject new];
}
或者说,线程的Runloop
注册了两个Observer
,
在Runloop
状态为kCFRunLoopEntry
时,autoreleasepool
会进行push
操作;
在Runloop
状态为kCFRunLoopBeforeWaiting
时,autoreleasepool
会先进行pop
操作,再进行push
操作;
在Runloop
状态为kCFRunLoopExit
时,autoreleasepool
会进行pop
操作。
(状态变化具体可看Runloop执行流程)
-
多层嵌套@autoreleasepool
@autoreleasepool {
@autoreleasepool {
@autoreleasepool {
}
}
}
要注意的是,多层嵌套并不是创建多个AutoreleasePoolPage
,而是使用同一个,只是每有一个@autoreleasepool
都会插入一个POOL_BOUNDARY
哨兵对象,当一个AutoreleasePoolPage
的大小不够用时,才会创建新的AutoreleasePoolPage
。

网友评论