Runloop
blog.csdn.net/u014600626/… juejin.cn/post/708193…
CFRunloop 对象
CFRunloop 结构
typedef struct __CFRunLoop *CFRunLoopRef;
struct __CFRunloop {
CFMutableSetRef _commonModes; //UITrackingRunLoopMode/kCFRunLoopDefaultMode
CFMutableSetRef _commonModeItems; //observer/time/source
CFRunLoopModeRef _currentMode; //当前运行的 mode
CFMutableSetRef _modes; // 集合 NSMutableSet <CFRunLoopMode *>
}
pthread: 与线程一一对应 currentMode: 当前的运行模式
CFRunloopMode 结构
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunloopMode {
CFStringRef _name;
CFMutableSetRef _source0;
CFMutableSetRef _source1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
}
面试题
Runloop 原理
-
通知 observers:RunLoop 要开始进入 loop 了,(kCFRunLoopEntry)
-
开启一个 do while 来保活线程,通知 observers :
- runloop 会触发 timers, source0 回调,(kCFRunLoopBeforeTimers kCFRunLoopBeforeSources)
- 接着执行加入的 block
- 接下来触发 Source0 回调,如果有 Source1是 ready 状态的话,就会跳到 handle_msg 去处理消息,
-
通知 Observers:RunLoop 的线程将进入休眠(sleep)状态 (kCFRunLoopBeforeWaiting)
-
进入休眠后,会等待 mach_port的消息,以再次唤醒;只有在下面四个事件
- 基于 port 的 source 事件
- timer 时间到
- runloop 超时
- 被调用者唤醒
-
唤醒时,通知 Observer: Runloop 的线程刚刚被唤醒了 (kCFRunLoopAfterWaiting)
-
被唤醒后,开始处理消息
-
如果是 Timer 时间到的话,就触发 Timer 的回调;
-
如果是 dispatch 的话,就执行 block;
-
如果是 source1 事件的话,就处理这个事件。
消息执行完后,就执行到loop里的block
-
-
根据当前runloop的状态来判断是否需要走下一个 loop。当被外部强制停止或loop 超时,就不继续下一个loop了,否则继续走下一个loop
Runloop 内部实现逻辑
Runloop是通过内部维护的事件循环来对事件/消息进行管理的一个对象
这里有两个重点
- 事件循环
- 事件/消息进行管理
什么是事件循环呢?
事件循环(状态切换)
没有消息需要处理时,休眠以避免资源占用
用户态——>内核态
有消息需要处理时,立刻被唤醒
用户态<—— 内核态
什么是事件/消息进行管理呢?
RunLoop 通过 mach_msg()函数接收、发送消息来进行管理。
它的本质是调用函数 mach_msg_trap(),相当于是一个系统调用,会触发内核状态切换。
可以做到在有事做的时候做事,没事做的时候,会由用户态切换到内核态,避免资源浪费。
如何实现事件、消息的管理
mach_msg() 函数实际上是调用了一个 Mach 陷阱 (trap), 即函数mach_msg_trap(),
陷阱这个概念在 Mach 中等同于系统调用。
当你在用户态调用 mach_msg_trap() 时会触发陷阱机制,切换到内核态;
内核态中内核实现的 mach_msg() 函数会完成实际的工作,
Runloop 休眠的实现原理
用户态和内核态之间的相互切换
mach_msg()
用户态 ----> 内核态 (等待消息)
内核态 ---->用户态 (处理消息)
内核态:
等待消息
没有消息就让线程休眠
有消息就唤醒线程
Runloop 和线程的关系
一个运行着的程序就是一个进程或者一个任务。每个进程至少有一个线程,线程就是程序的执行流。创建好一个进程的同时,一个线程便同时开始运行,也就是主线程。每个进程有自己独立的虚拟内存空间,线程之间共用进程的内存空间。有些线程执行的任务是一条直线,起点到终点;在 iOS 中,圆型的线程就是通过run loop不停的循环实现的。
1.每个线程包括主线程都有与之对应的 runloop 对象,线程和 runloop 对象是一一对应的;
2.Runloop 保存在一个全局字典中,线程为key, runloop为value CFDictionaryGetValue
3.主线程会默认开启 runloop , 子线程默认不会开启,需要手动开启
4.runloop 在第一次获取时创建,在线程结束时销毁
timer 和 Runloop 的关系
Runloop 是怎么相应用户操作的,具体操作流程是什么
首先由Source1捕捉系统事件,然后包装成eventqueue,传递给Source0处理触摸事件
Runloop的几种状态
Entry :
beforeTimers :
beforeSources
beforeWaiting
afterWaiting
exit
Runloop 的mode作用是什么
mode作用是用来隔离, 将不同组的Source0、Source1、timer、Observer 隔离开来,互不影响
主要有
defaultMode : app的默认 mode,通常主线程在这个mode下运行
UITrackingMode : 界面追踪 mode, 用于scrollview追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
mode 是怎么切换的
CFRunLoopRunSpecific 操作系统进行 mode 的切换;
是启动Runloop 和指定 Runloop 在哪个mode下执行的mode;
每一个 mode 处理完成后,如果 runloop 没有退出,就会返回之前的 mode,初始 mode 是 default;
CFRunLoopRunSpecific 会保持前一次 mode 的状态属性(stopped 和 currentmode),
然后发出即将要进入新的 mode 通知,然后进入 __CFRunLoopRun (__CFRunLoopRun 会创建一个循环),
然后这个 mode 运行结束后再发已退出 mode 通知,再恢复前一次的 stoppped 和 currentmode
Runloop 在实际开发中的作用
控制线程生命周期(线程保活)
检测应用卡顿
性能优化
other
viewDidLoad和viewWillAppear在同一个RunLoop循环中
UIApplicationMain 启动了 runloop