iOS晋级知识汇总(十一)RunLoop

RunLoop

  • 概念
  • 数据结构
  • 事件循环机制
  • RunLoop与NSTimer
  • RunLoop与多线程

什么是RunLoop?

  • 是通过内部维护的事件循环来对事件/消息进行管理的一个对象

    • 没有消息需要处理的时候,休眠以避免资源占用
    • 有消息需要处理时,立刻被唤醒
  • 没有消息需要处理时,休眠以避免资源占用

用户态->内核态

  • 有消息需要处理时,立刻被唤醒

内核态->用户态

用户态、内核态

用户态就是用开发使用的API都是用户态,需要用到系统内核的时候就是用到内核态,
每一个用户,也就是App都能让手机开关机,这种行为是不被允许的,所有才有一个用户态到内核态的切换,

什么是事件循环?

维护的事件循环可以让我们不断的处理消息和事件,对他们进行管理,当没有消息进行处理的时候,会发生一些用户态到内核态的切换。由此来进行当前线程的休眠,同时会发生内核态到用户态的切换,然后当前用户线程会被唤醒。

我们的main函数为什么能保持不退出?

在我们的main函数中会调用UIApplicationMain这个函数,这个函数里面做了一个运行循环(RunLoop),不断的接收事件,滑动列表处理事件的返回,处理消息完成以后会继续等待,这个循环不是简单的for循环而是从用户态到内核态的切换(休眠),从内核态到用户态的切换(唤醒)。

RunLoop的数据结构

NSRunLoop是对CFRunLoop的封装,提供了面向对象的API。

CFRunLoop的代码是开源的,可以通过这个开源看到CFRunLoop的数据结构

  • CFRunLoop
    • pthread ->代表线程 一对一
    • currentMode CFRunLoopMode的数据结构
    • modes MutableSet<CFRunLoopMode>
    • commonModes MutableSet<NSString>
    • commonModeItems MutableSet<Observers/Timers/Sources>
  • CFRunLoopMode
    • name mode的名字,通过名称来查找是哪一个mode
    • sources0 MutableSet
    • sources1 MutableSet
    • observers
    • timers
  • Source/Timer/Observer

CFRunLoopSource

  • sources0
    • 需要手动唤醒线程
  • sources1
    • 具备唤醒线程的能力

CFRunLoopTimer

基于事件的定时器,和NSTimer和toll-free bridged

CFRunLoopObserver

观测时间点:

  • kCFRunLoopEntry 准备启动的时候系统会给一个回调
  • kCFRunLoopBeforeTimers runloop将要对timer一些事件进行处理
  • kCFRunLoopBeforeSources 将要处理一些source事件
  • kCFRunLoopBeforeWaiting 将要进入休眠状态,即将发生用户态到内核态的一个转换
  • kCFRunLoopAfterWaiting 内核态切换到用户态不久
  • kCFRunLoopExit RunLoop退出

各个数据结构之间的关系

  • RunLoop和线程是一一对应的
  • 一个RunLoop可以拥有多个Mode
  • 一个mode可以有多个source、Timer、Observer

Runloop的Mode

mode跟mode中的事件是不会相互影响的,当我把事件添加到mode1上面运行的时候在mode2上面那么mode1的事件永远不会执行,mode和mode之间是孤立的。

我在tableview滚动的时候,你的banner控件不会自动切换,这就是RunLoop的mode的一种形式。

NSRunLoopCommonModes

  • 并不是实际存在的模式
  • 是同步source/timer/Observer到多个mode的一种技术方案。

NSRunLoop、CFRunLoop的Run方法

void CFRunLoopRun()

RunLoop启动的流程

  • 1、即将进入Runloop
  • 2、将处理Timer/source0事件
  • 3、处理Source0事件
  • 4、如果有source1要处理 跳转到8
  • 5、线程将要休眠
  • 6、休眠,等待唤醒
    • source1可以唤醒
    • Timer事件可以唤醒
    • 外部手动唤醒
  • 7、线程刚被唤醒
  • 8、处理唤醒时收到的消息 这一步会直接跳转到2中

RunLoop的核心

  • 用户态到内核态 休眠
  • 内核态到用户态 唤醒

滑动TableView的时候我们的定时器还会生效吗?

当运行的Timer是用默认start运行的timer,那么timer的RunLoop的mode是defaultMode,当用户滑动TableView的时候,当前运行的RunLoop的mode会切换到tracking这个mode,这就造成了Timer运行事件的隔离,最终导致Timer事件在华东过程中,没有任何反应。解决方式是,把timer添加到commonmodes这个runloop中。commonmodes并不是一个实际的runloopmode,他只是把一些mode打上commonmode标记,然后可以把某一个是事件源,比如说是timer同步到多个mode当中。

RunLoop与多线程

  • 线程和RunLoop一一对应的
  • 自己创建的线程默认是没有RunLoop的

怎么实现一个常驻的线程

  • 为当前线程开启一个Runloop (获取当前RunLoop,如果当前没有,则会自动创建)
  • 为该RunLoop中添加一个Port/source等维持RunLoop
    • 如果一个RunLoop中间没有item(Port或者Source)资源的话,是维持不住RunLoop的,会直接退出
  • 启动该RunLoop

代码如下

    // 创建一个Source
    CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
    CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
    
    // 创建RunLoop,同时向RunLoop的DefaultMode下面添加Source
    CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
    
    // 如果可以运行
    while (runAlways) {
        @autoreleasepool {
            // 令当前RunLoop运行在DefaultMode下面
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
        }
    }
    
    // 某一时机 静态变量runAlways = NO时 可以保证跳出RunLoop,线程退出
    CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
    CFRelease(source);

面试题

什么是RunLoop,他是怎么做到有事做事,没事休息

  • 是通过内部事件循环来对事件和消息进行管理的一个对象
  • 这个事件循环,不是简单的for循环,而是有事做事,没事休眠的一个循环,避免浪费系统资源
  • RunLoop的休眠和唤醒是系统对于用户态和内核态进行切换造成的

RunLoop与线程是怎么的关系?

RunLopp和线程是一一对应的。
新创建的线程模式是没有RunLoop的

如何实现一个常驻线程?

  • 创建一个线程对应的RunLoop
  • 向这个RunLoop中添加一个Source或者timer、observer、或者port
  • 最后调用CFRunLoopRunInMode

运行的模式和添加的的模式应该属于一个mode这样才能实现常驻线程

怎么保证子线程数据回来更新UI不打断用户滑动操作?

用户滑动操作实际上是在RunLoop中的trackingMode下,只要我们保证子线程的数据更新UI的时候处于defalutmode下,就不会影响用户动画操作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1.网络 1.网络七层协议有哪些? 物理层:主要功能:传输比特流;典型设备:集线器、中继器;典型协议标准和应用:V...
    _我和你一样阅读 3,503评论 1 38
  • 一:runloop相关知识 1.runloop是什么 runloop是通过内部维护的时间循环,来对事件/消息进行管...
    唯忆青语阅读 235评论 0 0
  • Runloop 通过内部维护事件循环来对事件/消息进行管理的一个对象。 没有消息处理时,进入休眠以避免资源占用 有...
    木子奕阅读 791评论 0 2
  • 一、内存管理 1. 引用计数 OC类中实现了引用计数器,对象知道自己当前被引用的次数。 对象初始化时计数器为1,每...
    邢罗康阅读 1,398评论 0 3
  • 注:本篇博客只在 ibireme 的 深入理解RunLoop 基础上做了点方便自己复习该知识点的修改,能力有限,如...
    AidenRao阅读 2,882评论 6 26