RunLoop

178 阅读4分钟

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 原理

  1. 通知 observers:RunLoop 要开始进入 loop 了,(kCFRunLoopEntry)

  2. 开启一个 do while 来保活线程,通知 observers :

    • runloop 会触发 timers, source0 回调,(kCFRunLoopBeforeTimers kCFRunLoopBeforeSources)
    • 接着执行加入的 block
    • 接下来触发 Source0 回调,如果有 Source1是 ready 状态的话,就会跳到 handle_msg 去处理消息,
  3. 通知 Observers:RunLoop 的线程将进入休眠(sleep)状态 (kCFRunLoopBeforeWaiting)

  4. 进入休眠后,会等待 mach_port的消息,以再次唤醒;只有在下面四个事件

    • 基于 port 的 source 事件
    • timer 时间到
    • runloop 超时
    • 被调用者唤醒
  5. 唤醒时,通知 Observer: Runloop 的线程刚刚被唤醒了 (kCFRunLoopAfterWaiting)

  6. 被唤醒后,开始处理消息

    • 如果是 Timer 时间到的话,就触发 Timer 的回调;

    • 如果是 dispatch 的话,就执行 block;

    • 如果是 source1 事件的话,就处理这个事件。

      消息执行完后,就执行到loop里的block

  7. 根据当前runloop的状态来判断是否需要走下一个 loop。当被外部强制停止或loop 超时,就不继续下一个loop了,否则继续走下一个loop

Runloop 内部实现逻辑

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

这里有两个重点

  1. 事件循环
  2. 事件/消息进行管理
什么是事件循环呢?

事件循环(状态切换)

 没有消息需要处理时,休眠以避免资源占用

            用户态——>内核态

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

            用户态<—— 内核态
            
什么是事件/消息进行管理呢? 

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 是 defaultCFRunLoopRunSpecific 会保持前一次 mode 的状态属性(stopped 和 currentmode),
然后发出即将要进入新的 mode 通知,然后进入 __CFRunLoopRun (__CFRunLoopRun 会创建一个循环),
然后这个 mode 运行结束后再发已退出 mode 通知,再恢复前一次的 stoppped 和 currentmode

Runloop 在实际开发中的作用

控制线程生命周期(线程保活)

检测应用卡顿

性能优化

other

viewDidLoadviewWillAppear在同一个RunLoop循环中

UIApplicationMain 启动了 runloop