一、简单图示
二、补充
一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。
RunLoop 内部的逻辑
1.1 RunLoop 实际上就是一个对象,这个对象管理需要处理的事件和消息,线程对象是执行事件和消息所对应的任务
1.2 RunLoop默认有五种mode, 每种mode都定义各自的sources0/sources1/observers/timers,分别实现对事件产生的回调,对时间触发的回调,对RunLoop活动观察的回调.
每次运行Runloop,都要指定一个运行模式(显示地或者隐式地)。在Runloop的运行期间,只有和当前运行模式相关的源才能被监控和允许发送事件。相似的,只有和当前运行模式相关的观察者才会被通知Runloop的行为。和其他模式相关的源会保留新的事件直到Runloop运行在了合适的模式才会分发。
2.RunLoop对象提供一个入口函数(do-while循环),当线程调用该函数后就会一直停留在这个循环里;直到超时或被手动停止,该函数才会返回.
线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有(苹果不允许直接创建 RunLoop)。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)。
3.在RunLoop的回调(最终在内核中实现)中线程去完成相关触发任务.
例:当一个硬件事件(触摸/锁屏/摇晃等)发生后,触发相应的RunLoop回调,然后在相应的线程中处理时间.
4.当调用 NSObject 的 performSelecter:afterDelay: 后,实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop(需要手动创建)(即 没有对时间触发的回调),则这个方法会失效。
5.App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。
第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池(autoreleasePool)。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。
在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool 了。
6.对于每一个Runloop, 系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个像CallStack一样的一个栈式结构,在每一个Runloop结束时(系统级别的栈式结构,不属于哪个线程),当前栈顶的Autorelease pool会被销毁,这样这个pool里的每个Object会被release(待验证:当对象被加入Autorelease pool时其引用计数+1;当Autorelease pool会被销毁时Object被release引用计数再-1,引用计数为0对象被释放)。
7.对于未创建RunLoop的线程,手动创建的Autorelease pool会在线程执行完任务后退出时销毁.
三、相关参考
1.图解进程线程
1.2 多线程例子
2.1 深入理解RunLoop
2.11 多线程之NSRunloop
2.12 NSRunloop原理