面试总结 - AutoreleasePool自动释放池

实现原理:

AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成
AutoreleasePool是按线程一一对应的
AutoreleasePoolPage每个对象会开辟4096字节内存
id *next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置
一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象在新的page加入。

释放时刻:

1.手动干预释放时机:手动指定autoreleasepool的autorelease对象,在当前作用域大括号结束时释放。
2.系统自动去释放:当对象出了作用域之后,会被添加到最近一次创建的自动释放池中,并会在当前的runloop迭代结束时释放。

每当进行一次objc_autoreleasePoolPush调用时,runtime向当前的AutoreleasePoolPage中add进一个哨兵对象,值为0(也就是个nil)
objc_autoreleasePoolPush的返回值正是这个哨兵对象的地址,被objc_autoreleasePoolPop(哨兵对象)作为入参
1.根据传入的哨兵对象地址找到哨兵对象所处的page
2.在当前page中,将晚于哨兵对象插入的所有autorelease对象都发送一次- release消息,并向回移动next指针到正确位置

嵌套的AutoreleasePool:

pop的时候总会释放到上次push的位置为止,多层的pool就是多个哨兵对象而已

App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。
第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。
在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool 了。

注意点:

子线程的 runloop 默认是不工作,无法主动创建,必须手动创建。
自定义的 NSOperation 和 NSThread 需要手动创建自动释放池。比如:自定义的 NSOperation 类中的 main 方法里就必须添加自动释放池。否则出了作用域后,自动释放对象会因为没有自动释放池去处理它,而造成内存泄露。
但对于 blockOperation 和 invocationOperation 这种默认的 Operation ,系统已经帮我们封装好了,不需要手动创建自动释放池。

发布了218 篇原创文章 · 获赞 19 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/songzhuo1991/article/details/104883414