概念
AutoreleasePool(自动释放池)
是OC
中的一种内存自动回收机制
,它可以延迟
加入AutoreleasePool中的变量release
的时机。在正常情况下,创建的变量会在超出其作用域
的时候release
,但是如果将变量加入AutoreleasePool
,那么release
将延迟
执行。
App
启动后,苹果在主线程 RunLoop
里注册了两个 Observer
,其回调都是_wrapRunLoopWithAutoreleasePoolHandler()
,从程序启动到加载
完成,主线程对应的runloop
会处于休眠
状态,等待用户交互
来唤醒runloop
- 第一个
Observer
监视的事件是Entry(即将进入Loop)
,其回调内会调用_objc_autoreleasePoolPush()
创建自动释放池
。优先级最高,保证创建释放池发生在其他所有回调之前
- 第二个
Observer
监视了两个事件:BeforeWaiting(准备进入休眠)
时调用_objc_autoreleasePoolPop()
和_objc_autoreleasePoolPush()
释放旧的池并创建新池;Exit(即将退出Loop)
时调用_objc_autoreleasePoolPop()
来释放自动释放池。优先级最低,保证其释放池子发生在其他所有回调之后
。
- 第一个
- 用户的每一次交互都会启动一次
runloop
,用于处理用户的所有点击、触摸事件
等 runloop
在监听到交互事件后,就会创建自动释放池
,并将所有延迟释放的对象
添加到自动释放池中主线程执行的代码
,通常是写在诸如事件回调、Timer回调
内的。这些回调会被RunLoop
创建好的AutoreleasePool
环绕着,所以不会出现内存泄漏
,开发者也不必显示创建Pool
了。- 在一次完整的runloop结束之前,会向自动释放池中所有对象发送release消息,然后销毁自动释放池
clang 分析
int main(int argc, char * argv[]) {
@autoreleasepool {
}
}
通过clang编译成cpp文件插看实现:
xcrun -sdk iphonesimulator clang -arch x86_64 -rewrite-objc main.m
int main(int argc, char * argv[]) {
/* @autoreleasepool */ {
__AtAutoreleasePool __autoreleasepool;
}
}
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
通过代码可以看出autoreleasepool
在底层实际是调用__AtAutoreleasePool
,而__AtAutoreleasePool
本质上是一个结构体
,其内部包含构造函数__AtAutoreleasePool()
与析构函数~__AtAutoreleasePool()
,在{}作用域
结束后会自动调用析构函数
,以便及时创建销毁
###汇编分析
struct LGTest {
LGTest(){
printf("1123 - %s",__func__);
}
~LGTest(){
printf("5667 - %s",__func__);
}
};
int main(int argc, char * argv[]) {
LGTest LGTest;
}
在main函数中添加断点查看汇编
可以看出跟clang编译后
一样都是经过objc_autoreleasePoolPush
与objc_autoreleasePoolPop
底层原理
在objc源码中是这样注释的
Autorelease pool implementation
- A thread's autorelease pool is a stack of pointers.
线程的自动释放池是指针的堆栈
- Each pointer is either an object to release, or POOL_BOUNDARY which is an autorelease pool boundary.
每个指针都是要释放的对象,或者是POOL_BOUNDARY(哨兵),它是自动释放池的边界。
- A pool token is a pointer to the POOL_BOUNDARY for that pool. When the pool is popped, every object hotter than the sentinel is released.
池令牌是指向该池的POOL_BOUNDARY的指针。弹出池后,将释放比哨点更热的每个对象。
- The stack is divided into a doubly-linked list of pages. Pages are added and deleted as necessary.
堆栈分为两个双向链接的页面列表。根据需要添加和删除页面。
- Thread-local storage points to the hot page, where newly autoreleased objects are stored.
线程本地存储指向热页面,该页面存储新自动释放的对象。
查看源码
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
NEVER_INLINE
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
通过代码可以看出push
与pop
操作都是基于AutoreleasePoolPage
,根据其定义看出自动释放池是页结构
,每页的大小为4096字节
//************宏定义************
#define PAGE_MIN_SIZE PAGE_SIZE
#define PAGE_SIZE I386_PGBYTES
#define I386_PGBYTES 4096 /* bytes per 80386 page */
//************类定义************
class AutoreleasePoolPage : private AutoreleasePoolPageData
{
friend struct thread_data_t;
public:
//页的大小
static size_t