美文网首页
进阶之路必读-RunLoop

进阶之路必读-RunLoop

作者: 来金德瑞 | 来源:发表于2016-07-25 17:09 被阅读17次

什么是RunLoop

RunLoop从字面意思上理解,就是运行循环的意思,它的基本作用就是保持程序的持续运行,处理App中的比如触摸、定时、Selector等等事件。RunLoop可以节省CPU的资源,提高程序的性能等等作用。

如果没有RunLoop

1. int main(int agrc,char *argv[]){
2.      NSLog(@"execute main function");//程序开始
3.      return 0;//程序结束
4. }

如果有了RunLoop

1. int main(int argc,char *argv[]){
2.      BOOL running = YES;
3.      do{
4.      //处理各种任务,处理各种事件
5.      while(running);
6.      return 0;
7. }}
  • 在有了RunLoop的情况下,由于main函数里面启动了个RunLoop,所以程序并不会马上推出,保持持续运行状态

main函数中的RunLoop

1. int main(int agrc,char *argv[]){
2.      @autoreleasepool{
3.          return UIApplicationMain(argc,argv,nil,NSStringFromClass*([AppDelegate class]));
4.      }
5. }
  • UIApplicationMain函数内部就启动了一个RunLoop
  • 所以UIApplicationMain函数一直没有返回,保持了程序的持续运行
  • 这个默认启动的RunLoop是跟主线程相关连的

RunLoop对象

  • iOS中有两套API来访问和使用RunLoop
    • Foundation -> NSRunLoop
    • Core Foundation -> CFRunLoopRef
  • NSRunLoop和CFRunLoop都代表着RunLoop对象
  • NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)
  • 每一条线程都有唯一的一个与之对应的RunLoop对象
  • 主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
  • RunLoop在第一次获取时创建,在线程结束时销毁

获取RunLoop对象

  • Fundation
    • [NSRunLoop currentRunLoop];//获取当前线程的RunLoop对象
    • [NSRunLoop mainRunLoop];//获取主线程的RunLoop对象
  • Core Foundation
    • CFRunLoopGetCurrent();//获取当前线程的RunLoop对象
    • CFRunLoopGetMain();//获得主线程的RunLoop对象

RunLoop的相关类

  • Core Foundation中关于RunLoop的5个类
    1. CFRunLoopRef
    2. CFRunLoopModeRef
    3. CFRunLoopSourceRef
    4. CFRunLoopTimerRef
    5. CFRunLoopObserverRef

CFRunLoopModeref

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

CFRunLoopSourceRef

  • CFRunLoopSourceRef是事件源(输入源)
    • Source0:非基于Port
    • Source1:基于Port

CFRunLoopTimerRef

  • CFRunLoopTimerRef是基于时间的触发器
  • 基本上说的就是NSTimer

CFRunLoopObserRef

  • CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变

  • 可以监听的时间点有一下几个

      typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
          kCFRunLoopEntry = (1UL << 0),//即将进入Loop
          kCFRunLoopBeforeTimers = (1UL << 1),//即将处理Timer
          kCFRunLoopBeforeSources = (1UL << 2),//即将处理Source
          kCFRunLoopBeforeWaiting = (1UL << 5),//即将进入休眠
          kCFRunLoopAfterWaiting = (1UL << 6),//刚从休眠中唤醒
          kCFRunLoopExit = (1UL << 7),//即将退出Loop
          kCFRunLoopAllActivities = 0x0FFFFFFFU
      };
    

RunLoop处理逻辑

官方版

  • 每次运行RunLoop,你的线程的RunLoop会自动处理之前未处理的消息,并通知相关的观察者。具体如下:
    1. 通知观察者run loop已经启动
    2. 通知观察者任何即将要开始的定时器
    3. 通知观察者任何即将启动的非基于端口的源
    4. 启动任何准备好的非基于端口的源
    5. 如果基于端口的源准备好并处于等待状态,立即启动,并且进入步骤9
    6. 通知观察者线程进入休眠
    7. 将线程置于休眠直到任意下面的事情发生:
      • 某一事件到达基于端口的源
      • 定时器启动
      • run loop设置的时间已经超时
      • run loop被娴熟唤醒
    8. 通知观察者线程将被唤醒
    9. 处理未处理的事件
      • 如果用户定义的定时器被启动,处理定时器事件并重启。进入步骤2
      • 如果处理输入源启动,传递相应的消息
      • 如果run loop被显示唤醒而且时间还没超过,重启run loop。进入步骤2
    10. 通知观察者run loop结束

网友整理版

RunLoop小节

  • RunLoop整理:
    1. 从字面意思看:运行循环、跑圈,其实它内部就是do-while循环,就这个循环内部不断地处理各种任务(比如Source、Timer、Observer)。
    2. 一个线程对应一个RunLoop,主线程的RunLoop默认已经启动,子现场的RunLoop得手动启动。
    3. RunLoop只能选择一个模式Mode启动,如果当前Mode中没有任何Source、Timer、Observer,那么就直接退出RunLoop
  • 自动释放池什么时候释放?(在RunLoop睡眠之前释放(kCFRunLoopBeforeWaiting))
  • RunLoop如何使用于应用场景
    1. 让一个子线程不进入消亡状态,等待从其它线程发来消息,处理其它事情
    2. 在子线程中开启一个定时器
    3. 在子线程中长期监控一些行为
    4. 可以控制定时器在哪种模式下运行
    5. 可以让某些事件(行为、任务)在特定模式下执行
    6. 可以添加Observer监听RunLoop的状态,比如监听点击事件的处理(在所有点击事件致前做点事情)

相关文章

网友评论

      本文标题:进阶之路必读-RunLoop

      本文链接:https://www.haomeiwen.com/subject/fjagjttx.html