Autorelease Pool 回收机制 AutoreleasePool(自动释放池)是OC中的一种内存自动回收机制,它可以延迟 加入AutoreleasePool中的变量release的时机 创建和释放 时机 接下来从main函数入口看下整个流程: main 函数入口 int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } 从代码上看,整个主线程都被包含在 autoreleasepool 大括号内 因此,主线程上的对象,都在自动回收机制内,不会出现内存泄漏,不需要 手动 release了 创建主线程 接着 UIApplicationMain 内部开始创建一条主线程 Runloop 接着主动访问runloop,接着主线程的runloop 开始被创建 Runloop 之 Observer RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler() 第一个 Observer 监视的事件是 Entry(即将进入Loop) 调用回调: _wrapRunLoopWithAutoreleasePoolHandler() 回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池 autoreleasepool push()优先级最高,回调中其它回调都在push结束后执行,确保代码块都在auto release pool 范围内 第二个 Observer 第一个监听: BeforeWaiting(准备进入休眠) 调用_objc_autoreleasePoolPop() 释放旧的池 调用pop()后,释放池开始释放对象 接着调用_objc_autoreleasePoolPush() 并创建新池 第二个监听: Exit(即将退出Loop) 调用 _objc_autoreleasePoolPop() 来释放自动释放池 优先级最低,确保退出之前 释放工作全部完成 不再创建新的池 小结: 线程 依赖Runloop 维持生命,一一对应。 而Runloop又依赖 一个主 autoreleasePool 来管理对象 , 也是一一对应 开发者也可以在主 autoreleasePool 中,嵌套更多的子 autoreleasePool 来管 理局部代码块的释放时机,比如for循环中临时对象释放 至此,释放池创建与释放逻辑基本完成,子线程类似,只是Runloop 需要开发者 主动获取 实现原理 从上面结构中可以看到,AutoreleasePool 是AutoreleasePoolPage ,一个 C+ + 中的类 一次流程: push() --> [objs release] --> pop(obj) Push() void *objc_autoreleasePoolPush(void) { return AutoreleasePoolPage::push(); } Pop(void *) void objc_autoreleasePoolPop(void *ctxt) { AutoreleasePoolPage::pop(ctxt); } AutoreleasePoolPage 到底如何实现的? 结构 AutoreleasePoolPage { magic_t const magic; //magic用来校验Page结构是否完整; id *next; //指向新加入的autorelease对象 pthread_t const thread; //thread指向当前的线程 AutoreleasePoolPage * const parent; //父节点 指向前一个page AutoreleasePoolPage *child; //子节点 指向下一个page uint32_t const depth; //链表的深度,节点个数 uint32_t hiwat; //数据容纳的一个上限 static pthread_key_t const key = AUTORELEASE_POOL_KEY; static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing static size_t const SIZE = PAGE_MAX_SIZE; //虚拟内存每个扇区4096个 字节,4K对齐 #if PROTECT_AUTORELEASEPOOL PAGE_MAX_SIZE; // must be multiple of vm page size #else PAGE_MAX_SIZE; // size and alignment, power of 2 #endif static size_t const COUNT = SIZE // 一个page里对象数 } 双向链表 AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双 向链表的形式组合而成的栈结构(分别对应结构中的parent指针和child指针) 一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage 对象,连接链表,后来的autorelease对象在新的page加入 示意图 Page 1 指针 parent -->page0 child -->page2 Page 2 指针 parent -->page1 child -->page3 Page 3 指针 parent -->page2 child -->page4 等等 Push 流程 push() 代码 Runloop 回调:_wrapRunLoopWithAutoreleasePoolHandler() 调用 objc_autoreleasePoolPush() void *objc_autoreleasePoolPush(void) { return AutoreleasePoolPage::push(); } push() static inline void *push() { return autoreleaseFast(POOL_BOUNDARY); } autoreleaseFast : 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); } } hotPage() 就是指当前 page autoreleaseFast(id obj) 中的id 是 push生成的边界对象 push() 逻辑 【无 hotPage】 调用 autoreleaseNoPage 创建一个 hotPage,调用 page->add(obj) 方法将对 象添加至 AutoreleasePoolPage 的栈中 【有 hotPage 并且当前 page 不满】 调用 page->add(obj) 方法将对象添加至 AutoreleasePoolPage 的栈中 【有 hotPage 并且当前 page 已满】 调用 autoreleaseFullPage 初始化一个新的页,调用 page->add(obj) 方法将 对象添加至 AutoreleasePoolPage 的栈中 假设右侧嵌套结构 @AutoreleasePool { // 1层 obj1, obj2; @AutoreleasePool { // 2层 obj3, obj4 @AutoreleasePool { // 3层 obj5, obj6 } } } 嵌套push() 示意图: 高地址 -------- Page 4Kb -------- 低地址 push 高地址 -------- Page 4Kb obj2 obj1 POOL_BOUNDARY(边界对象) -------- 低地址 push 高地址 -------- Page 4Kb obj4 obj3 POOL_BOUNDARY obj2 obj1 POOL_BOUNDARY -------- 低地址 简单说就是每次push都会从低地址向高地址: 插入边界 - 插入obj - 插入obj2 - , 如果一页放不下了,就新建一页 嵌套pop(obj) 释放就简单了,从高地址的obj 开始释放,直到 边界对象为止,这样一个pool 释放完成 该过程主要分为两步: - page->releaseUntil(stop),对栈顶(page->next)到stop地址 (POOL_SENTINEL)之间的所有对象调用objc_release(),进行引用计数减1 - 清空page对象page->kill(),有两句注释