AutoReleasePool 是什么
1. 自动释放池 ,是OC中的一种内存自动回收机制
2. 它可以延迟加入AutoreleasePool中的变量release的时机。MRC中,调用[obj autorelease],ARC下,@autoreleasepool{}管理
3. 自动释放池是一个个 AutoreleasePoolPage 组成的
一个page是4096字节大小,
每个 AutoreleasePoolPage 以双向链表连接起来形成一个自动释放池
当对象调用 autorelease 方法时,会将对象加入 AutoreleasePoolPage 的栈中
初始化调用push 时是把边界对象放到栈顶, 然后对page 中添加的对象等操作
pop 时是传入边界对象,然后对page 中的对象发送release 的消息
创建与释放时机
- App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。
(添加监听)
- 第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。
其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
(创建+优先级最高)
- 第二个 Observer 监视了两个事件:
BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;
Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。
(释放重建+优先级最低)
如何实现
- 苹果通过声明一个__AtAutoreleasePool类型的局部变量__autoreleasepool
实现了@autoreleasepool{}。
- 单个自动释放池的执行过程就是objc_autoreleasePoolPush() —> [object autorelease] —> objc_autoreleasePoolPop(void *)
- objc_autoreleasePoolPush 和 objc_autoreleasePoolPop 是 AutoreleasePoolPage 对应静态方法 push 和 pop 的封装
结构组成
AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成(分别对应结构中的parent指针和child指针)
- 一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象在新的page加入
- 向一个对象发送- autorelease消息,就是将这个对象加入到当前AutoreleasePoolPage的栈顶next指针指向的位置
- AutoreleasePool是按线程一一对应的(结构中的thread指针指向当前线程)
AutoreleasePoolPage结构
- AutoreleasePoolPage每个对象会开辟4096字节内存4k(也就是虚拟内存一页的大小),除了上面的实例变量所占空间,剩下的空间全部用来储存autorelease对象的地址
- 上面的id *next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置
- 栈顶的 哨兵对象(POOL_SENTINEL/边界对象)
objc_autoreleasePoolPush
- Push时都会把边界对象放进栈顶,然后返回边界对象,用于释放。
- 1. atautoreleasepoolobj = objc_autoreleasePoolPush();
- 2. AutoreleasePoolPage::push
- 3. autoreleaseFast(POOL_BOUNDARY)
- 有 hotPage 并且当前 page 不满,调用 page->add(obj) 方法将对象添加至Page 的栈中
- 有 hotPage 并且当前 page 已满,调用 autoreleaseFullPage 初始化一个新的页,调用 page->add(obj) 方法将对象添加至Page 的栈中
- 无 hotPage,调用 autoreleaseNoPage 创建一个 hotPage,调用 page->add(obj) 方法将对象添加至 Page 的栈中
- 关键代码都是调用autoreleaseFast函数向自动释放池的链表栈中添加一个对象,
objc_autoreleasePoolPop
- 1. objc_autoreleasePoolPop(atautoreleasepoolobj);
atautoreleasepoolobj 就是一个 POOL_SENTINEL 自动释放池释放 是 传入 push 返回的边界对象/哨兵对象, Pop 调用时,就会向自动释放池中的对象发送 release 消息,直到第一个 POOL_SENTINEL:(向 pop 方法传入非哨兵参数是可行的,只是我们一般不会传入非哨兵对象。) - 2. AutoreleasePoolPage::pop(ctxt);
- 3. autoreleaseFast(POOL_SENTINEL);
- 1、使用 pageForPointer 获取当前 token 所在的 AutoreleasePoolPage pageForPointer 方法主要是通过内存地址的操作,获取当前指针所在页的首地址:
- 2、调用 releaseUntil 方法释放栈中的对象,直到 stop
- 3,调用 child 的 kill 方法
- /2 最后调用的方法 fastCheck() 用来检查当前的 result 是不是一个 AutoreleasePoolPage,releaseUntil 释放对象
- kill() 方法 当前页面以及子页面全部删除
其他实例:
- 使用容器的block版本的枚举器时,内部会自动添加一个AutoreleasePool:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {// 这里被一个局部@autoreleasepool包围着
}];
当然,在普通for循环和for in循环中没有,
所以,还是新版的block版本枚举器更加方便。
for循环中遍历产生大量autorelease变量时,就需要手加局部AutoreleasePool咯。