一、什么是Runloop?
Runloop是通过内部维护的事件循环,来对事件\消息进行管理的对象。
二、什么是事件循环?
有消息需要的处理的时,立即被唤醒,(内核态-->用户态)
没有消息需要处理时,进入休眠状态,避免资源占用。(用户态-->内核态)
维护的事件循环:可以用来不断的处理消息或者事件,然后对他们进行管理,同时,当没有消息需要处理时,会从用户态到内核态的一个切换,以此进行当前线程的休眠,避免资源占用。同时当有消息需要处理时,就会发生从内核态到用户态的转换。当前的用户线程会被唤醒。
三、我们的main 函数为什么可以一直处于活跃状态,不退出?
在main 函数中多调用的UIApplicationMain 这个函数内部,会启动主线程runloop,runloop是对事件循环的维护机制。可以在有事做,做事,没有事做,进行用户态到内核态的切换,进入休眠,避免占用资源。
四、Runloop 的数据结构:
NSRunLoop(fundation) 是CFRunLoop的封装,提供了面向对象的API.
(1)NSRunLoop 的数据结构:
- CFRunLoop
- CFRunLoopMode
- Timer/Observer/Source
(2)CFRunLoop 结构体
![](https://img.haomeiwen.com/i13647581/c6d3b8e1e7bdc2e3.png)
CFRunLoop包含有:
- pthread 线程与runloop 是一一对应的关系。
- currentMode、------>CFRunLoopMode 的数据结构。
- modes(多个mode的集合) ----> NSMutableSet< CFRunLoopMode *>
- commonModes----->NSMutableSet< NSString *>
- commonModeItems --->包含多个timer,多个source,多个observer。
注意:runloop与mode 是一对多的关系。
** CFRunLoopMode结构**
- name. ====> NSDefaultRunLoopMode(别名定义的字符串)
- sources0
- soources1
- observers
- timers
CFRunLoopSource结构
- source0 需要手动唤醒线程能力。
- source1 具备唤醒线程的能力。
CFRunLoopTimer
事件定时器:和NSTimer 是toll-free bridge的。
CFRunLoopObserver结构体
观测时间点:
- kCFRunLoopEntry. 将要进入runloop 中。
- kCFRunLoopBeforeTimers 将要处理timers 的相关问题
- kCFRunLoopBeforeSources 将要处理source相关问题。
- kCFRunLoopBeforeWaiting 通知观察者将要进入休眠状态(即将:用户态-->内核态)
- kCFRunLoopAfterWaiting 内核态-->用户态的不久时间点。
- kCFRunLoopExit. 退出runloop.
数据结构之间的关系:
- runloop与其mode方式是一对多的方式,而Mode与source /timer/observer都是一对多的。
![](https://img.haomeiwen.com/i13647581/3aa38296f509dfa0.png)
五、RunLoopMode 的数据结构:
![](https://img.haomeiwen.com/i13647581/823b9f300c3b9526.png)
mode为什么会有多个??
当我们的runloop运行在某个mode1时,另一个mode2上的timer事件进行回调了。这是runloop是无法接收对应mode2 中的事件回调。起到了屏蔽的效果。
如何将一个Timer添加到不同的mode下??可以使用CommonMode.
CommonMode结构特性
![](https://img.haomeiwen.com/i13647581/20a10423d8eaa55e.png)
??
CommonMode 是同步Source\Timer\Observer到多个mode的解决方案。
六、事件循环机制:
在runloop启动后,会通过通知,告诉观察者即将进入runloop. 之后,将要处理timer/source0事件通知。之后进入到正式的source0事件处理。如果有Source1 ,那么会通过goto语法,处理唤醒时,收到的消息。如果没有什么要处理的。就会进入休眠。唤醒操作:timer/source1/外部手动唤醒。线程被唤醒后,会触发观察者,告诉观察者,线程被唤醒了。即将推出RunLoop.
![](https://img.haomeiwen.com/i13647581/090007a6b36296e6.png)
七、RunLoop 与NSTimer
(1)滑动TableView 的时候,我们的定时器还会生效吗??
正常情况下,是kCFRunLoopDefaultModel.当滑动时进行了mode 转换,转换成了UITractingRunLoopMode;
通过CFRunLoopAddTimer(runloop, timer,commonMode)commonMode 不是实际的Mode,只是将Mode打上一个CommonMode 的标记。就可以将一个事件元,同步到多个Mode 中。
八、线程与RunLoop
(1)自己创建的线程是没有runloop的需要我们手动创建。
(2)如何实现一个常驻线程?
- 为当前线程开启RunLoop.(调用获取RunLoop 的方法,因为获取RunLoop的方法,会判定是否有,没有就进行创建)
- 向该runloop中添加一个port/source 等用来维护RunLoop事件循环。(资源事件元,需要处理的话,默认情况下,是不能维持事件循环的)
- 启动RunLoop.
1.创建一个Source
- 创建RunLoop 并将Source 添加到runLoop 中。(CFRunLoopAddSource)
3。维护循环,再循环中创建AutoReleasePool,并调用CFRunLoopRunInMode 方法。**注意:添加资源的Mode,与运行时Mode的必须是同一个。
4.移除source,调用CFRunLoopRemoveSource(某一时机可以保证RunLoop线程推出。)
5.释放资源CFRelease(source)避免内存泄漏。
(3)调用RunLoop的run方法后,系统会调用mach_message方法,发生了用户态向核心态的切换。当前线程处于休眠状态。
(4)如何保证子线程数据更新UI的时候,不打断用户滑动操作?
分析:在用户处于滑动的情况下,处于RunLoop的UITractingMode的模式下。因此,当子线程获取到数据需要显示UI时,我们可以将数据进行包装后提交到住线程的DefaultMode 模式下。这样当滑动时,由于处于TractingMode下,就不会对其进行处理,知道滑动停止后,回到defaultMode时,才开始对其进行处理,这样就不会打断用户的滑动效果了。
实现原理:当App启动时,会调用UIApplicationMain,启动主线程中的RunLoop.
经过一些列处理后,runloop 将会处于休眠状态,此时,我点击屏幕,就会产生一个MachPort,machport会转化成Source1,唤醒主线程,运行处理,之后,当App杀死,就会发生runloop 的退出。也会发出一个通知,即将退出runloop ,退出后,线程被销毁。
RunLoop 核心问题?
![](https://img.haomeiwen.com/i13647581/cfa0e64bd3a4b068.png)
网友评论