RunLoop
1.RunLoop目的?
1.保证程序不退出
2.负责监听事件!(触摸,时钟,网络事件)
3. 睡眠中,等待消——处理消息
2.线程跟RunLoop有什么关系?
一对一关系
1、每条线程都有唯一的一个与之对应的RunLoop对象
2、RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
3、线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建([NSRunLoop currentRunLoop])
4、RunLoop会在线程结束时销毁
5、主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
3.RunLoop的组成
Runloop中可以存在多个mode,mode是一种运行场景。
Runloop一次只能在一种mode下运行,切换mode必须停止后重新开始运行。
默认Runloop至少带有
-
Default mode(默认模式,大部分操作均在此模式)
-
Event tracking mode(追踪用户拖动或者其他交互时使用)
-
Common modes(是一个模式集合,runloop会把加入commonItems的事件同步到common modes中的所有mode,默认集合中已有default mode和event tracking mode)
可以自己为runloop添加自定义mode。
每个mode封装了自己需要响应的事件和需要通知的observer。这些事件的形式有两种:timer和input source。另外可以在mode中注册observer来观察runloop的运行状态。
-
timer:给线程发送同步事件,但是是在未来指定时间发送或者周期性的响应事件。
-
source:给线程发送异步事件,分为两种,source0和source1。source0需要手动在另一个线程手动触发(通过调用
void CFRunLoopSourceSignal ( CFRunLoopSourceRef source );
),CFSocket就是这种形式;source1是基于mach port的事件,程序只要把message塞给port,底层就会触发runloop中对应source1。目前CFMachPort和CFMessagePort是这种形式。以上描述来自CFRunloopSource Reference -
observer:可以观察六种runloop的运行状态:
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),//进入runloop
kCFRunLoopBeforeTimers = (1UL << 1),//准备处理timer
kCFRunLoopBeforeSources = (1UL << 2),//准备处理source
kCFRunLoopBeforeWaiting = (1UL << 5),//准备休眠
kCFRunLoopAfterWaiting = (1UL << 6),//休眠结束,处理事件之前
kCFRunLoopExit = (1UL << 7),//runloop退出
kCFRunLoopAllActivities = 0x0FFFFFFFU//所有状态
};
这里要提一句的是,timer和source1(也就是基于port的source)可以反复使用,比如timer设置为repeat,port可以持续接收消息,而source0在一次触发后就会被runloop移除。
4.如何启动RunLoop
OSX/iOS 系统中,提供了两个这样的对象:NSRunLoop 和 CFRunLoopRef。
CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的。
NSRunLoop 是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的。
[NSRunLoop currentRunLoop];
5.程序中添加每3秒响应一次的NSTimer,当拖动tableview时timer可能无法响应要怎么解决?
runloop 常用的mode有两种 :NSDefaultRunLoopMode、UITrackingRunLoopMode
程序默认处于 NSDefaultRunLoopMode 模式下 但是页面滑动的时候 runloop的模式就会切换到 UITrackingRunLoopMode 此时就会退出 NSDefaultRunLoopMode ,在NSDefaultRunLoopMode模式下所有的操作都会被停止(如:NSTimer事件响应)。当页面停止滑动时 就会自动切换到NSDefaultRunLoopMode,(此时NSTimer才会继续工作 )
因此:想要在当拖动tableview时timer继续工作 就必须把 timer 添加到runloop任何模式都能工作。代码如下
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
NSRunLoopCommonModes:并不是一个真的模式,它只是一个标记
6.讲讲 RunLoop,项目中有用到吗?
1、控制线程生命周期(线程保活)
2、解决NSTimer在滑动时停止工作的问题
3、监控应用卡顿
4、性能优化
7.RunLoop的运行逻辑
Source0 作用
1、触摸事件处理
2、performSelector:onThread:
Source1 作用
1、基于Port的线程间通信
2、系统事件捕捉
Timers 作用
1、NSTimer
2、performSelector:withObject:afterDelay:
Observers 作用
1、用于监听RunLoop的状态
根据runloop的状态来做相应的事情 如:UI刷新(BeforeWaiting)、Autorelease pool(BeforeWaiting)等
流程
01、通知Observers:进入Loop
02、通知Observers:即将处理Timers
03、通知Observers:即将处理Sources
04、处理Blocks
05、处理Source0(可能会再次处理Blocks)
06、如果存在Source1,就跳转到第8步
07、通知Observers:开始休眠(等待消息唤醒)
08、通知Observers:结束休眠(被某个消息唤醒)
01> 处理Timer
02> 处理GCD Async To Main Queue
03> 处理Source1
09、处理Blocks
10、根据前面的执行结果,决定如何操作
01> 回到第02步
02> 退出Loop
11、通知Observers:退出Loop