UI 系统实际上是不断地处理各种各样的消息,你可以初步将 RunLoop 理解成下面循环
1(等待消息) -> 2(将要处理消息) -> 3(处理消息) -> 4(消息处理完成) -> 1(等待消息)
你也可以将消息(message)这个词换成信号(signal)或者事件(Event)。当没有消息到来的时候,这个线程就会休眠,等待消息到来后触发处理过程。
其中,AutoreleasePool 在 4 (消息处理完成) 这个阶段进行释放。iOS 开发者写的程序,无论包含多少层函数调用,也一直处于 3 (处理消息) 这个阶段当中,其余阶段属于 UI 框架的一部分。因此在当前消息循环内,放到 AutoreleasePool 的对象会一直生效,并不会被释放。
提问:主线程会经历无数次Runloop循环,那么我之前加入autoreleasepool的实例就会被释放,这就意味着我不能再使用之前的实例了?
回答:假如你的实例,并没有存储起来,到下一个循环就不能使用了。而假如你想在下一个事件循环使用之前的实例,就需要存储起来。AutoreleasePool 是跟引用计数配合起来使用的,实际上 AutoreleasePool 并不会真正释放对象,而只是减少一个引用计数。假如你之前的对象存储起来,引用不为 0,到下一个循环,对象也不会被释放。
记录 AutoreleasePool的创建和释放时机,和释放时做了什么。
---------------------------------------------------------------
autoReleasePool 什么时候释放?
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 了。
————————————————