iOS探究 --- Runloop
在研究runloop之前,先让我们了解一下程序,进程和线程。
程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。而线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
一般来说,一个线程一次只能执行一个任务,执行完成后线程就会退出。所以程序运行的时候,需要一个机制,让线程能随时处理事件但不退出。
这时候,Runloop出现了。
从字面意思来看,Runloop顾名思义是一个运行循环。
他是一个对象,拥有入口函数。do-while循环
Runloop的作用
1.保持程序的运行。
2.处理APP中的各种事件(触摸、定时器、performSelector)
3.节省cpu资源、提供程序的性能:该做事就做事,该休息就休息。
线程与Runloop的关系
线程和Runloop之间是一一对应的,甚至在苹果文档里,Runloop都是写在线程章节里的。
他们的对应关系保存在一个Dictionary里。子线程的Runloop需要手动开启。
image.png
NSTimer为什么会不准呢?
image.png这要从Runloop最核心的结构类型说起。
系统默认注册了5个Mode:
(1)kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。
(2)UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
(3)UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
(4)GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。
(5)kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际作用。
Runloop拥有多个Mode,但是只能在一个Mode下运行。
Mode对应多个分支。
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
当NSTimer加载main runloop重,模式是NSDefaultRunloopMode,当有UI操作和复杂的运算时,runloop中的timer就会产生阻塞。
当你创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个ScrollView时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作。所以就会影响到NSTimer不准的情况。
source0 ,source1
Source 有两个版本:Source0 和 Source1,
Source0 只包含一个函数指针,并不能主动触发,需要将 Source0 标记为待处理,在 RunLoop 运转的时候,才会处理这个事件(如果 RunLoop 处于休眠状态,则不会被唤醒去处理)。Source0主要处理app内部事件,app自己负责管理的事务,如UI点击事件。
Source1 包含了一个 mach_port 和一个函数指针,mach_port 是 iOS 系统提供的基于端口的输入源,可用于线程或进程间通讯。而 RunLoop 支持的输入源类型中就包括基于端口的输入源,可以做到对 mach_port 端口源事件的监听。所以监听到 source1 端口的消息时,RunLoop 就会自己醒来去执行 Source1 事件(也能称为被消息唤醒)。也就是 Source0 是直接添加给 RunLoop 处理的事件,而 Source1 是基于端口的,进程或线程之间传递消息触发的事件。