如何理解Runloop
RunLoop是系统内部维护的事件循环(EventLoop)来对消息/事件进行处理的一个对象。
事件循环(EventLoop):没有消息处理时,休眠以避免资源占用(从用户态切换到内核态)。有消息处理时,线程立刻被唤醒(从内核态切换到用户态)。
Runloop的数据结构
RunLoop的数据结构主要包括以下几个部分:CFRunLoop、CFRunLoopMode、Source/Timer/Observer
1. CFRunLoop:这是RunLoop的主要组成部分,负责整个线程的执行管理。
CFRunLoop:
pthread:一一对应(线程与Runloop的关系)
currentMode:CFRunloopMode
modes:NSMutableSet<CFRunloopMode*> Runloop与mode是一对多的关系
commonModes:NSMutableSet<NSString*>
commonModeItems:多个Observer、Timer、Source的集合
2.CFRunloopMode:
name:NSDefaultRunloopMode
sources0:CFRunloopSource集合
sources1:CFRunloopSource集合
observers:数组
timers:数组
以上可以看出mode与source、timer、observer也是一对多的关系
3.CFRunloopSource:
source0:需要手动唤醒线程
source1:有主动唤醒线程的能力
4.CFRunloopTimer:
基于事件的定时器,与NSTimer有免费桥转换(tool-free bridge)
5.CFRunloopObserver:
观测时间点
kCFRunloopEntry
kCFRunloopBeforeTimers
kCFRunloopBeforeSources
kCFRunloopBeforeWaiting
kCFRunloopAfterWaiting
kCFRunloopExit
Runloop具体怎样维护的事件循环EventLoop?
RunLoop使用do-while循环来处理事件队列中的事件。当没有新事件时,RunLoop会调用
pthread_cond_wait
进入内核态,让出CPU控制权给其他线程;当有新事件进入队列时,RunLoop会调用pthread_cond_signal()产生信号
或pthread_cond_broadcast()广播至线程
重新开始处理这些事件。
Runloop的mode有哪几种
共有五种运行模式,分别是:
kCFRunLoopDefaultMode
:这是App的默认Mode,通常主线程是在这个Mode下运行。UITrackingRunLoopMode
:这是界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响。UIInitializationRunLoopMode
:这是在刚启动App时进入的第一个Mode,启动完成后就不再使用,会切换到kCFRunLoopDefaultMode
。GSEventReceiveRunLoopMode
:这是接受系统事件的内部Mode,通常用不到。kCFRunLoopCommonModes
:这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode
和UITrackingRunLoopMode
。
请注意,最后一种Mode——commonMode
不是一种真正的Mode,而是一种特定的标识类型Mode,是一种同步Source/Timer/Observer到多种模式中的一种技术方案。在其标识的类型Mode中,添加事件处理意味着在其中的模式中均可以处理事件的回调。一般默认的commonMode中包含有defaultMode、trackingMode还有modalMode,可使用CFRunLoopAddCommonMode
方法向CommonModes中添加自定义Modes。
系统中有哪些地方用到了Runloop
- UI线程:iOS的UI更新只在主线程上执行。因此,为了在UI上显示一些更新,你需要将更新放在主线程的RunLoop中。例如,如果你有一个自定义的动画或者你需要在用户交互后更新一些UI元素,你可能需要使用RunLoop。
- 定时器:NSTimer是iOS中创建定时器的类。这个类使用了RunLoop来处理定时任务。当你创建一个NSTimer对象后,它会被添加到RunLoop中,这样就可以在指定的时间间隔后触发一个回调。
- 触摸事件:iOS设备上的触摸事件(例如,用户点击屏幕或者移动手指)也是通过RunLoop处理的。这些事件首先被送到应用程序的主线程,然后由RunLoop进行处理。
- 异步处理:如果你正在进行一些异步处理(例如,从网络加载数据),你可能会使用RunLoop来处理这些任务。你可以创建一个新的NSOperation或者使用Grand Central Dispatch来在后台线程上执行任务,然后在任务完成后将结果传递回主线程的RunLoop。
- 定时任务:如果你需要定期执行一些任务(例如,定期检查应用程序的状态或者定期更新应用程序的数据),你可能会使用RunLoop来处理这些任务。你可以创建一个新的NSTimer对象,然后将其添加到RunLoop中。
Runloop与线程的关系
RunLoop与线程的关系非常密切,它们是一一对应的关系。每条线程,包括程序的主线程,都有与之相应的RunLoop对象。
- 线程创建:当线程刚创建时,并没有RunLoop对象。RunLoop会在第一次获取它时创建。
- Runloop启动:主线程的RunLoop默认是启动的,子线程的RunLoop默认是不开启的,需要我们自己手动开启循环。
- 线程运行:在线程运行过程中,RunLoop会负责维护线程的状态(唤醒、休眠),包括处理输入事件、调度任务、更新UI等。
- 线程结束:当线程执行完全部任务或者手动停止时,RunLoop会随之销毁。RunLoop销毁时机是在线程结束时。