文章目录

  • 什么是RunLoop
  • 基本作用
  • 如果没有RunLoop
  • 如果有了RunLoop
  • main函数中的RunLoop
  • RunLoop对象
  • RunLoop资料
  • RunLoop与线程
  • 如何保证RunLoop和线程一一对应
  • 获得RunLoop对象
  • RunLoop相关类
  • CFRunLoopModeRef
  • CFRunLoopTimerRef
  • CFRunLoopSourceRef
  • CFRunLoopObserverRef
  • RunLoop处理逻辑
  • RunLoop应用
  • 关于RunLoop自动释放池
  • RunLoop面试题
  • 总结


什么是RunLoop

  • 从字面意思看:运行循环、跑圈
  • 其实它内部就是do-while循环,在这个循环内部不断处理各种任务(比如Source、Timer和Observer)

基本作用

  • 保持程序的持续运行
  • 处理App中的各种事件(比如触摸事件、定时器时间、Selector事件
  • 节省CPU资源,提高程序性能:该做事时做事,该休息时休息

如果没有RunLoop

1 int main(int argc, char *argv[]){
2    NSLog(@"execute main function"); ——>程序开始
3    return 0;                        ——>程序结束
4 }
  • 没有RunLoop的情况下,程序执行到第三行代码就结束了。

如果有了RunLoop

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

main函数中的RunLoop

ios开发 pop到指定controller ios开发 runloop_知识点

  • 第14行代码的UIApplicationMain函数内部就启动了一个RunLoop
  • 所以UIApplicationMain函数一直没有返回,保持了程序的持续运行
  • 这个默认启动的RunLoop是跟主线程相关联的

RunLoop对象

  • iOS中有2套API来访问和使用RunLoop:
  1. Foundation框架:
    NSRunLoop
  2. Core Foundation:CFRunLoopRef
  • NSRunLoop和CFRunLoopRef都代表着RunLoop对象
  • NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)

RunLoop资料

RunLoop与线程

  • 每一条线程都有唯一一个与之对应的RunLoop对象
  • 主线程的RunLoop已经创建好了,子线程的RunLoop需要主动创建
  • RunLoop在第一次获取时创建,在线程结束时销毁

如何保证RunLoop和线程一一对应

  • 源码中是通过字典存储,线程作为Key,RunLoop作为Value。

获得RunLoop对象

  • Foundation
[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的运行模式
  1. 一个RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer。
  2. 每次RunLoop启动时,只能指定一个Mode,这个Mode被称为CurrentMode。
  3. 如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入(这个过程由系统自动完成)。
  4. 这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互补互不影响。
  • 系统默认注册了5个Mode:
  1. kCFRunLoopDefaultMode:App的默认Mode,通常主线程在这个Mode下运行。
  2. UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其它Mode影响。
  3. UIInitializationRunLoopMode:在刚启动App时进入的第一个Mode,启动完成后不再使用。
  4. GSEventReceiveRunLoopMode:接收系统事件的内部Mode,通常用不到。
  5. kCFRunLoopCommonModes:这是一个占位用的Mode,不是一种真正的Mode(凡是用到这个模式,意味着同时使用kCFRunLoopDefaultModeUITrackingRunLoopMode模式)。

CFRunLoopTimerRef

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

CFRunLoopSourceRef

  • CFRunLoopSourceRef是事件源(输入源)
  • 以前的分法:
  1. Port-Based Sources
  2. Custom Input Sources
  3. Cocoa Perform Selector Sources
  • 现在的分法:
  1. Source0:非基于Port的(用户主动触发的时间,例如点击一个按钮触发事件)
  2. Source1:基于Port的(系统触发事件)

CFRunLoopObserverRef

  • CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),
    kCFRunLoopBeforeTimers = (1UL << 1),
    kCFRunLoopBeforeSources = (1UL << 2),
    kCFRunLoopBeforeWaiting = (1UL << 5),
    kCFRunLoopAfterWaiting = (1UL << 6),
    kCFRunLoopExit = (1UL << 7),
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

RunLoop处理逻辑

ios开发 pop到指定controller ios开发 runloop_RunLoop_02


ios开发 pop到指定controller ios开发 runloop_iOS_03


ios开发 pop到指定controller ios开发 runloop_知识点_04

RunLoop应用

  • NSTimer
  • ImageView显示
  • PerformSelector
  • 常驻线程
  • 自动释放池

关于RunLoop自动释放池

  • 自动释放池第一次创建:当runloop启动的时候
  • 自动释放池最后一次销毁:当runloop退出的时候
  • 自动释放池其他时间的创建和销毁:当runloop即将进入到休眠的时候,会把之前的自动释放池释放,重新创建一个新的自动释放池

RunLoop面试题

ios开发 pop到指定controller ios开发 runloop_知识点_05


ios开发 pop到指定controller ios开发 runloop_移动开发_06

总结

ios开发 pop到指定controller ios开发 runloop_移动开发_07