多线程 - 12.RunLoop总结

1.RunLoop概述

  • 参照苹果官方文档,官方API文档
  • RunLoops是线程相关的的基础框架的一部分。一个run loop就是一个事件处理的循环,用来不停的调度工作以及处理输入事件。使用run loop可以让你的线程在有任务的时候执行任务,而没任务的时候处于休眠状态。

  • 基本作用如下:

    • 保持程序的持续运行
    • 处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)
    • 节省CPU资源,提高程序性能:该做事时做事,该休息时休息
  • 另外,网上有很多翻译官方SDK的文档,这里就不做过多的介绍了


2.模拟系统RunLoop操作

  • 如果没有RunLoop,如下程序执行完打印操作后,继续执行到return后,程序就退出了
int main(int argc, char * argv[]) {
    NSLog(@"execute main function");
    return 0;
}
  • 有了RunLoop,程序会进入死循环,一直执行,不会退出
int main(int argc, char * argv[]) {
    BOOL running = YES;
    do {
        // 执行各种任务,处理各种事件
             // ......
    } while (running);
    return 0;
}
  • 在我们创建iOS项目后,系统会自动在main函数里面启动了RunLoop,所以运行程序并不会马上退出,而是一直保持持续运行状态
  • main函数中的RunLoop
// UIApplicationMain函数中会开启一个RunLoop且和主线程绑定
int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

3.RunLoop对象

  • iOS中有2套API来访问和使用RunLoop
    • 一个是Foundation框架,利用NSRunLoop来访问RunLoop对象
    • 第二套框架是Core Foundation,利用CFRunLoopRef来访问RunLoop对象
    • NSRunLoop和CFRunLoopRef都代表着RunLoop对象
  • NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,了解底层实现原理,这里会重点说明CFRunLoopRef层面的API(Core Foundation层面),另外CFRunLoopRef的源代码是开源的,所以可以了解内部的实现原理
  • CFRunLoopRef的开源代码可以从以下地址下载,CFRunLoopRef源码下载地址

4.RunLoop与线程

  • 每条线程都有唯一的一个与之对应的RunLoop对象
  • 主线程的RunLoop在系统启动时会默认帮我们自动创建好,且和主线程关联起来了,而子线程的RunLoop需要主动创建
  • RunLoop在第一次获取时创建,在线程结束时销毁
  • RunLoop对象是懒加载的,所以要想创建子线程的RunLoop对象,直接调用当前RunLoop对象方法,系统就会帮我们自动创建一个RunLoop对象,并且和当前子线程关联起来
  • 获得RunLoop对象
// Foundation框架
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
// Core Foundation框架
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象

5.RunLoop相关类

  • Core Foundation中关于RunLoop的5个类
    • CFRunLoopRef(RunLoop对象)
    • CFRunLoopModeRef(RunLoop运行模式)
      • CFRunLoopSourceRef(RunLoop事件源)
      • CFRunLoopTimerRef(RunLoop定时器)
      • CFRunLoopObserverRef(RunLoop观察者)
  • 下图可以说明RunLoop的内部结构模式
    这里写图片描述

6.CFRunLoopModeRef运行模式

  • CFRunLoopModeRef代表RunLoop中的运行模式
    • 一个 RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer
    • 每次RunLoop启动时,只能指定其中一个Mode,这个Mode被称作CurrentMode(当前运行模式)
    • 如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入
    • 这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响
  • 系统默认注册了5个Mode:
    • kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
    • UITrackingRunLoopMode:界面跟踪 Mode,用于ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
    • UIInitializationRunLoopMode:在刚启动App时第进入的第一个 Mode,启动完成后就不再使用
    • GSEventReceiveRunLoopMode:接受系统事件的内部Mode,通常用不到
    • kCFRunLoopCommonModes:这是一个占位用的Mode,不是一种真正的Mode

7.CFRunLoopSourceRef(事件源或者输入源)

  • 以前的分法
    • Port-Based Sources(基于接口的输入源)
    • Custom Input Sources(自定义接口的输入源)
    • Cocoa Perform Selector Sources(任一线程执行函数输入源)
  • 现在的分法
    • Source0:非基于Port的
    • Source1:基于Port的
  • 输入源向线程发送异步消息。消息来源取决于输入源的种类:基于端口的输入源和自定义输入源。基于端口的源监听程序相应的端口,而自定义输入源则关注自定义的消息。至于run loop,它不关心输入源的种类。系统会去实现两种源供你使用。两类输入源的区别在于如何显示的:基于端口的源由内核自动发送,而自定义的则需要人工从其他线程发送。

8.CFRunLoopTimerRef(定时源)

  • CFRunLoopTimerRef是基于时间的触发器,基本上说的就是NSTimer
  • 定时源在预设的时间点同步地传递消息。定时器时线程通知自己做某事的一种方法。例如,搜索控件可以使用定时器,当用户连续输入的时间超过一定时间时,就开始一次搜索。这样,用户就可以有足够的时间来输入想要搜索的关键字
  • 尽管定时器和时间有关,但它并不是实时的。和输入源一样,定时器也是和run loop的运行模式相关联的。如果定时器所在的模式未被run loop监视,那么定时器将不会开始直到run loop运行在相应的模式下。类似的,如果定时器在run loop处理某一事件时开始,定时器会一直等待直到下次run loop开始相应的处理程序。如果run loop不再运行,那定时器也将永远不开始。

9.CFRunLoopObserverRef(观察者)

  • CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变
  • 源是同步或异步的传递消息,而RunLoop观察者则是在运行RunLoop的时候在特定的时候开始。你可以使用RunLoop观察者来为某一特定事件或是进入休眠的线程做准备
  • 可以监听的时间点有以下几个
    这里写图片描述

10.RunLoop处理逻辑

  • 官方版
    这里写图片描述
  • 逻辑解释
  • 每次运行 Run loop,你线程的 Run loop 对会自动处理之前未处理的消息,并通知相关的观察者。具体的顺序如下:
    1. 通知观察者 Run loop 已经启动。
    2. 通知观察者任何即将要开始的定时器。
    3. 通知观察者任何即将启动的非基于端口的源。
    4. 启动任何准备好的非基于端口的源。
    5. 如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤 9。
    6. 通知观察者线程进入休眠。
    7. 将线程置于休眠直到任一下面的事件发生:
      • 某一事件到达基于端口的源;
      • 定时器启动;
      • Run loop 设置的时间已经超时;
      • Run loop 被显式唤醒。
    8. 通知观察者线程将被唤醒。
    9. 处理未处理的事件
      • 如果用户定义的定时器启动,处理定时器事件并重启 Run loop。进入步骤 2。
      • 如果输入源启动,传递相应的消息。
      • 如果 Run loop 被显式唤醒而且时间还没超时,重启 Run loop,进入步骤 2。
    10. 通知观察者 Run loop 结束。
  • 网友整理版
    这里写图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值