Runloop 实现机制

一、什么是Runloop?

Runloop是通过内部维护的事件循环,来对事件\消息进行管理的对象。
二、什么是事件循环?

有消息需要的处理的时,立即被唤醒,(内核态-->用户态)
没有消息需要处理时,进入休眠状态,避免资源占用。(用户态-->内核态)

维护的事件循环:可以用来不断的处理消息或者事件,然后对他们进行管理,同时,当没有消息需要处理时,会从用户态到内核态的一个切换,以此进行当前线程的休眠,避免资源占用。同时当有消息需要处理时,就会发生从内核态到用户态的转换。当前的用户线程会被唤醒。

三、我们的main 函数为什么可以一直处于活跃状态,不退出?
在main 函数中多调用的UIApplicationMain 这个函数内部,会启动主线程runloop,runloop是对事件循环的维护机制。可以在有事做,做事,没有事做,进行用户态到内核态的切换,进入休眠,避免占用资源。

四、Runloop 的数据结构:

NSRunLoop(fundation) 是CFRunLoop的封装,提供了面向对象的API.

(1)NSRunLoop 的数据结构:

  • CFRunLoop
  • CFRunLoopMode
  • Timer/Observer/Source

(2)CFRunLoop 结构体

结构体

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都是一对多的。
数据结构

五、RunLoopMode 的数据结构:

mode 数据结构

mode为什么会有多个??

当我们的runloop运行在某个mode1时,另一个mode2上的timer事件进行回调了。这是runloop是无法接收对应mode2 中的事件回调。起到了屏蔽的效果。

如何将一个Timer添加到不同的mode下??可以使用CommonMode.

CommonMode结构特性


结构特性

??
CommonMode 是同步Source\Timer\Observer到多个mode的解决方案。

六、事件循环机制:

在runloop启动后,会通过通知,告诉观察者即将进入runloop. 之后,将要处理timer/source0事件通知。之后进入到正式的source0事件处理。如果有Source1 ,那么会通过goto语法,处理唤醒时,收到的消息。如果没有什么要处理的。就会进入休眠。唤醒操作:timer/source1/外部手动唤醒。线程被唤醒后,会触发观察者,告诉观察者,线程被唤醒了。即将推出RunLoop.

屏幕快照 2020-01-13 上午1.12.08.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

  1. 创建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 核心问题?

RunLoop 核心

你可能感兴趣的:(Runloop 实现机制)