RunLoop 原理和核心机制
搞iOS之后一直没有深入研究过RunLoop,非常的惭愧。刚好前一阵子负责性能优化项目,需要利用RunLoop做性能优化和性能检测,趁着这个机会深入研究了RunLoop的原理和特性。
RunLoop的定义
当有持续的异步任务需求时,我们会创建一个独立的生命周期可控的线程。RunLoop就是控制线程生命周期并接收事件进行处理的机制。
RunLoop是iOS事件响应与任务处理最核心的机制,它贯穿iOS整个系统。
Foundation: NSRunLoop
Core Foundation: CFRunLoop 核心部分,代码开源,C 语言编写,跨平台
目的
通过RunLoop机制实现省电,流畅,响应速度快,用户体验好
理解
进程是一家工厂,线程是一个流水线,Run Loop就是流水线上的主管;当工厂接到商家的订单分配给这个流水线时,Run Loop就启动这个流水线,让流水线动起来,生产产品;当产品生产完毕时,Run Loop就会暂时停下流水线,节约资源。
RunLoop管理流水线,流水线才不会因为无所事事被工厂销毁;而不需要流水线时,就会辞退RunLoop这个主管,即退出线程,把所有资源释放。
RunLoop并不是iOS平台的专属概念,在任何平台的多线程编程中,为控制线程的生命周期,接收处理异步消息都需要类似RunLoop的循环机制实现,Android的Looper就是类似的机制。
特性
- 主线程的RunLoop在应用启动的时候就会自动创建
- 其他线程则需要在该线程下自己启动
- 不能自己创建RunLoop
- RunLoop并不是线程安全的,所以需要避免在其他线程上调用当前线程的RunLoop
- RunLoop负责管理autorelease pools
- RunLoop负责处理消息事件,即输入源事件和计时器事件
RunLoop机制
主线程 (有 RunLoop 的线程) 几乎所有函数都从以下六个之一的函数调起:
CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION
CFRunloop is calling out to an abserver callback function
用于向外部报告 RunLoop 当前状态的更改,框架中很多机制都由 RunLoopObserver 触发,如 CAAnimationCFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK
CFRunloop is calling out to a block
消息通知、非延迟的perform、dispatch调用、block回调、KVOCFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
CFRunloop is servicing the main desipatch queueCFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION
CFRunloop is calling out to a timer callback function
延迟的perform, 延迟dispatch调用CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION
CFRunloop is calling out to a source 0 perform function
处理App内部事件、App自己负责管理(触发),如UIEvent、CFSocket。普通函数调用,系统调用CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION
CFRunloop is calling out to a source 1 perform function
由RunLoop和内核管理,Mach port驱动,如CFMachPort、CFMessagePortRunLoop 架构

- RunLoop 运行时
主要有以下六种状态:
- kCFRunLoopEntry -- 进入runloop循环
- kCFRunLoopBeforeTimers -- 处理定时调用前回调
- kCFRunLoopBeforeSources -- 处理input sources的事件
- kCFRunLoopBeforeWaiting -- runloop睡眠前调用
- kCFRunLoopAfterWaiting -- runloop唤醒后调用
- kCFRunLoopExit -- 退出runloop
RunLoop 运行时调用栈
- 主线程App运行时
- RunLoopObserver与Autorelease Pool的关系
UIKit 通过 RunLoopObserver 在 RunLoop 两次 Sleep 间对 Autorelease Pool 进行 Pop 和 Push 将这次 Loop 中产生的 Autorelease 对象释放。
- RunLoop的挂起与唤醒
指定用于唤醒的 mach_port 端口
调用 mach_msg 监听唤醒端口,被唤醒前系统内核将这个线程挂起,停留在mach_msg_trap状态。
由另一个线程向内核发送这个端口的msg后,trap状态被唤醒,RunLoop继续工作。
RunLoop支持的消息事件(Events)
- RunLoop
支持接收处理输入源(Input Source)事件,包括:
系统的Mach Port事件,是一种通讯事件
自定义输入事件支持接受处理定时源(Timer)事件
在启动RunLoop之前,必须添加监听的输入源事件或者定时源事件,否则调用[runloop run]会直接返回,而不会进入循环让线程长驻。
如果没有添加任何输入源事件或Timer事件,线程会一直在无限循环空转中,会一直占用CPU时间片,没有实现资源的合理分配。
没有while循环且没有添加任何输入源或Timer的线程,线程会直接完成,被系统回收。
//错误做法
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
while (!self.isCancelled && !self.isFinished) { [runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]]; }; //正确做法 NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; while (!self.isCancelled && !self.isFinished) { @autoreleasepool { [runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]]; } }
Run Loop Modes
理解
Run Loop Mode就是流水线上支持生产的产品类型,流水线在一个时刻只能在一种模式下运行,生产某一类型的产品。消息事件就是订单。Cocoa定义了四中Mode
Default:NSDefaultRunLoopMode,默认模式,在Run Loop没有指定Mode的时候,默认就跑在Default Mode下
Connection:NSConnectionReplyMode,用来监听处理网络请求NSConnection的事件
Modal:NSModalPanelRunLoopMode,OS X的Modal面板事件
Event tracking:UITrackingRunLoopMode,拖动事件
Common mode:NSRunLoopCommonModes,是一个模式集合,当绑定一个事件源到这个模式集合的时候就相当于绑定到了集合内的每一个模式RunLoop可以通过[acceptInputForMode:beforeDate:]和[runMode:beforeDate:]来指定在一段时间内的运行模式。如果不指定的话,RunLoop默认会运行在Default下(不断重复调用runMode:NSDefaultRunLoopMode beforDate:)
在主线程启动一个计时器Timer,然后拖动UITableView或者UIScrollView,计时器不执行。这是因为,为了更好的用户体验,在主线程中Event tracking模式的优先级最高。在用户拖动控件时,主线程的Run Loop是运行在Event tracking Mode下,而创建的Timer是默认关联为Default Mode,因此系统不会立即执行Default Mode下接收的事件。解决方法:
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1.0target:selfselector:@selector(timerFireMethod:) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; //或 [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; [timer fire];
Run Loop应用实践
Run Loop主要有以下三个应用场景:
- 维护线程的生命周期,让线程不自动退出,isFinished为Yes时退出。
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
while (!self.isCancelled && !self.isFinished) { @autoreleasepool { [runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]]; } }
- 创建常驻线程,执行一些会一直存在的任务。该线程的生命周期跟App相同
@autoreleasepool {NSRunLoop *runLoop = [NSRunLoop currentRunLoop];[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; [runLoop run]; }
- 在一定时间内监听某种事件,或执行某种任务的线程
如下代码,在30分钟内,每隔30s执行onTimerFired:。这种场景一般会出现在,如我需要在应用启动之后,在一定时间内持续更新某项数据。
@autoreleasepool {NSRunLoop * runLoop = [NSRunLoop currentRunLoop];NSTimer * udpateTimer = [NSTimer timerWithTimeInterval:30 target:self selector:@selector(onTimerFired:) userInfo:nil repeats:YES]; [runLoop addTimer:udpateTimer forMode:NSRunLoopCommonModes]; [runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:60*30]]; }
- AFNetworking中RunLoop的创建
+ (void)networkRequestThreadEntryPoint:(id)__unused object {@autoreleasepool {[[NSThread currentThread] setName:@"AFNetworking"]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; // 这里主要是监听某个 port,目的是让这个 Thread 不会回收 [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; [runLoop run]; } } + (NSThread *)networkRequestThread { static NSThread *_networkRequestThread = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; [_networkRequestThread start]; }); return _networkRequestThread; }
转载于:https://www.cnblogs.com/idxdm/p/4614250.html
RunLoop 原理和核心机制相关推荐
- iOS RunLoop 原理和核心机制
RunLoop的定义 当有持续的异步任务需求时,我们会创建一个独立的生命周期可控的线程.RunLoop就是控制线程生命周期并接收事件进行处理的机制. RunLoop是iOS事件响应与任务处理最核心的机 ...
- 李洪强iOS开发之RunLoop的原理和核心机制
李洪强iOS开发之RunLoop的原理和核心机制 搞iOS之后一直没有深入研究过RunLoop,非常的惭愧.刚好前一阵子负责性能优化项目,需要利用RunLoop做性能优化和性能检测,趁着这个机会深入研 ...
- Java 运行原理和核心机制
一.Java初体验HelloWorld 1.1 实现步骤 将 Java 代码编写到扩展名为 .java 的文件中. 通过 javac 命令对该 java 文件进行编译. ...
- Vue、angular等框架实现双向绑定的原理,核心机制是使用了Object.defineProperty
<div id="name"></div> var obj = {};Object.defineProperty(obj, "name" ...
- 网络原理 | TCP/IP中的连接管理机制 重要协议与核心机制
应用层.传输层.网络层.数据链路层.物理层 一.应用层协议 应用层 是程序猿最最经常打交道的一层 其他四层,都是操作系统.驱动.硬件,实现好了的,咱们不需要管 (除非你是系统工程师,驱动开发工程师-- ...
- theoretical-零基础学Qt4编程之Qt核心机制与原理之信号与槽-
信号与槽 信号和槽机制是Qt的核心机制之一,要掌握Qt编程就需要对信号和槽有所了解.信号和槽是一种高级接口,它们被应用于对象之间的通信,它们是Qt的核心特性,也是Qt不同于其它同类工具包的重要地方之一 ...
- Android 插件化原理学习 —— Hook 机制之动态代理
前言 为了实现 App 的快速迭代更新,基于 H5 Hybrid 的解决方案有很多,由于 webview 本身的性能问题,也随之出现了很多基于 JS 引擎实现的原生渲染的方案,例如 React Nat ...
- Hadoop核心机制详细解析
Hadoop的核心机制是通过HDFS文件系统和MapReduce算法进行存储资源.内存和程序的有效利用与管理.在现实的实例中,通过Hadoop,可以轻易的将多台普通的或低性能的服务器组合成分布式的运算 ...
- 比特币的原理及运作机制
这篇文章的定位会比较科普,尽量用类比的方法将比特币的基本原理讲出来.这篇文章不会涉及算法和协议中比较细节的部分. 在这篇文章中我会给出一个虚拟的村庄叫"比特村",整个文章会以讲故事 ...
- ajax的原理和运行机制
关于ajax,是最近炒得非常火的一种技术,并且时下它也是非常流行.当然,它并不是什么新技术,而是在各种已有的技术和支持机制下的一个统一.在我的项目中,偶尔也会用到ajax,用来给用户一些无刷新的体验. ...
最新文章
- mysql的worker 线程_MySQL线程
- 用python打印九九乘法表while_利用Python循环(包括whilefor)各种打印九九乘法表的实例...
- Docker容器引导完整CentOS
- Java基础知识强化之集合框架笔记50:Map集合之Map集合的概述和特点
- WCF BasicHttpBinding 安全解析(1)BasicHttpBinding基本配置
- 哇!大开脑洞!“绿协杯”东莞市第六届绿色建筑设计比赛【往期获奖作品回顾上篇】
- Python使用combinations实现排列组合
- codeforces1467D. Sum of Paths
- Java泛型进阶篇: 无界通配符、上界通配符以及下界通配符
- 百度CarLife Android车机端黑屏问题
- 我的读书清单(持续更新)
- 推荐一款优秀的硬盘空间管理工具软件-TreeSize Free
- 还在手动换IP?大佬们都这样做
- 机器学习----线性回归
- vscode 实现在行尾加分号
- java套打pdf做模版itext填数据
- 各大网商店铺历史价格分析原理
- VRTK4 入门指南
- xp计算机远程桌面设置密码,win7远程桌面连接xp 图解win7远程管理xp桌面
- win7系统弹出appcrash错误解决方法