RunLoop小白入门

news2024/11/16 17:42:17

在这里插入图片描述

核心概念

什么是 RunLoop ?

RunLoop 是 iOS 和 macOS 应用程序框架中的一个核心概念,用于管理线程的事件处理。它可以看作是一个循环,用于持续接收和处理各种事件,如用户输入、定时器、网络事件等。RunLoop 在保持应用程序响应用户交互和系统事件方面起着关键作用。

网络上常见的 source1 具体指什么 ?

其实在苹果的官方文档中是没有这个概念的, 网络上大多数稍有深度的 RunLoop 文章, 基体都会提到 source1 这个概念, source1首次出现已无从考证, 其指代的是基于 Mach 端口的输入源, 两者指的是同一个东西.

在 macOS 和 iOS 中,很多系统事件是通过 Mach 端口传递的。Mach 是底层内核的一部分,提供了进程间通信(IPC)的机制。基于 Mach 端口的输入源(Source1)用于处理这种通信。

苹果用 RunLoop 实现的功能

结合 AutoreleasePool 实现自动清理

在主线程中, 每次RunLoop循环开始和结束时,系统会自动创建和销毁AutoreleasePool。这就像你在每次做完一顿饭后统一清理脏碗碟:

  • RunLoop开始:系统会自动创建一个新的AutoreleasePool,开始处理事件。
  • RunLoop循环中:你可能会创建很多临时对象,这些对象会被添加到当前的AutoreleasePool中。
  • RunLoop结束:系统会自动释放并销毁这个AutoreleasePool,清理所有在这个循环中创建的临时对象。

为什么需要这种机制?
这种机制保证了在每个事件循环结束时,所有临时对象都能被及时释放,避免内存泄漏。如果没有AutoreleasePool,你需要手动管理所有临时对象的释放,增加了代码复杂性和错误的风险。

事件响应

1. 创建 Mach 端口:

系统会为应用程序创建一个 Mach 端口,用于接收来自内核的事件。这些事件可能包括用户输入事件(如触摸、按键)和系统通知等。

2. 将 Mach 端口添加到 RunLoop:

应用程序会将这个 Mach 端口作为 RunLoop 的一个输入源进行注册 (source1)。这是通过创建一个基于端口的 CFRunLoopSourceRef(Core Foundation Run Loop Source)来实现的。

3. 事件的传递与处理:
  • 3.1. 当一个硬件事件(触摸/锁屏/摇晃等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收;

  • 3.2. SpringBoard 将接收的 IOHIDEvent 事件, 通过 mach port 转发给对应的 App 进程;

  • 3.3. 应用进程中的 Source1 被触发后,调用 __IOHIDEventSystemClientQueueCallback()。这个回调函数进一步调用 _UIApplicationHandleEventQueue() 进行应用内部的事件分发;

  • 3.4. _UIApplicationHandleEventQueue() 将 IOHIDEvent 事件转换成 UIEvent 事件进行处理或分发,其中包括触摸事件的处理(如 touchesBegan/Move/End/Cancel 事件)、按钮点击事件、手势识别(UIGestureRecognizer)等;

手势识别

当上面的 _UIApplicationHandleEventQueue() 识别了一个手势时,其首先会调用 Cancel 将当前的 touchesBegin/Move/End 系列回调打断。随后系统将对应的 UIGestureRecognizer 标记为待处理。

苹果注册了一个 Observer 监测 BeforeWaiting (Loop即将进入休眠) 事件,这个Observer的回调函数是 _UIGestureRecognizerUpdateObserver(),其内部会获取所有刚被标记为待处理的 GestureRecognizer,并执行GestureRecognizer的回调。

界面更新

当在操作 UI 时,比如改变了 Frame、更新了 UIView/CALayer 的层次时,或者手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,这个 UIView/CALayer 就被标记为待处理,并被提交到一个全局的容器去。

苹果注册了一个 Observer 监听 BeforeWaiting(即将进入休眠) 和 Exit (即将退出Loop) 事件,回调去执行一个很长的函数:
_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()。这个函数里会遍历所有待处理的 UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面。

定时器

在 iOS 中,RunLoop 和定时器(NSTimerCADisplayLink)有着密切的关系。定时器依赖于 RunLoop 来触发回调函数。

NSTimer 和 RunLoop
工作机制

NSTimer 是一个基于时间间隔的触发器,用于在指定的时间间隔之后向目标对象发送消息。它依赖于 RunLoop 来定期检查和触发, NSTimer 其实就是 CFRunLoopTimerRef,他们之间是 toll-free bridged 的。

  1. 创建定时器

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                      target:self
                                                    selector:@selector(timerFired:)
                                                    userInfo:nil
                                                     repeats:YES];
    
  2. 添加到 RunLoop
    当你使用 scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: 方法创建定时器时,它会自动添加到当前线程的默认 RunLoop 模式中。你也可以手动将 NSTimer 添加到 RunLoop 中:

    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    
  3. RunLoop 处理定时器
    RunLoop 每次循环时会检查定时器的触发时间。如果定时器的触发时间已到或已过,RunLoop 会触发定时器的回调方法(例如 timerFired:)。

RunLoop 模式对定时器的影响
  • 默认模式 (NSDefaultRunLoopMode):通常用于普通的事件处理。如果 NSTimer 被添加到此模式中,当用户进行滚动操作(导致 RunLoop 切换到 UITrackingRunLoopMode)时,定时器将暂停。
  • 通用模式 (NSRunLoopCommonModes):可以确保 NSTimer 在不同的 RunLoop 模式下都能触发。例如,在滚动视图时,RunLoop 会切换到 UITrackingRunLoopMode,如果定时器添加到通用模式,它仍然会触发。
CADisplayLink 和 RunLoop

CADisplayLink 是一个特殊的定时器,与屏幕刷新率同步,通常用于动画。

  1. 创建和添加 CADisplayLink

    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkFired:)];
    [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
  2. 屏幕刷新同步
    CADisplayLink 会在屏幕每次刷新时调用其目标对象的选择器方法。屏幕通常每秒刷新60次(即60Hz),因此 CADisplayLink 的回调方法也会以相同的频率调用。

  3. 处理动画
    在回调方法中,可以更新动画状态或执行其他需要在每帧更新的操作。由于它与屏幕刷新率同步,CADisplayLink 非常适合实现平滑的动画。

RunLoop 模式对 CADisplayLink 的影响

NSTimer 类似,如果 CADisplayLink 被添加到默认模式,则在滚动视图时可能会暂停。为确保它在滚动期间也能触发,可以将它添加到通用模式:

[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

PerformSelecter

在 iOS 中,RunLoop 和 performSelector 方法之间的关系主要体现在线程间通信和延迟执行这两个方面。RunLoop 负责调度 performSelector 方法的执行时机,确保方法在合适的时机被调用。

关于GCD

GCD 的绝大多数实现并不依赖于 RunLoop,它们是两个独立的机制,各自有不同的应用场景和实现方式。
但是, GCD 提供的某些接口也用到了 RunLoop, 例如 dispatch_async(), 当调用 dispatch_async(dispatch_get_main_queue(), block) 时,libDispatch 会向主线程的 RunLoop 发送消息,RunLoop会被唤醒,并从消息中取得这个 block,并在回调 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE() 里执行这个 block。但这个逻辑仅限于 dispatch 到主线程,dispatch 到其他线程仍然是由 libDispatch 处理的。

关于网络请求


RunLoop 的实际应用举例

ReactNative创建常驻JS线程

AFNetworking创建后台常驻线程接受回调任务

+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"AFNetworking"];
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}

AsyncDisplayKit在主线程的 RunLoop 中添加一个 Observer

ASDK 仿照 QuartzCore/UIKit 框架的模式,实现了一套类似的界面更新的机制:即在主线程的 RunLoop 中添加一个 Observer,监听了 kCFRunLoopBeforeWaiting 和 kCFRunLoopExit 事件,在收到回调时,遍历所有之前放入队列的待处理的任务,然后一一执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1792864.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker create rm export exec命令详解

容器生命周期管理命令教程-3 1. 创建容器 docker create:创建一个新的容器但不启动它。 docker create -it --name mycontainer ubuntu bash通常使用 docker run(详细可看上一篇关于run命令的详细介绍) 2. 删除容器 docker rm:删除一个或多个容器。 d…

T-Pot多功能蜜罐实践@debian12@FreeBSD

T-Pot介绍 T-Pot是一个集所有功能于一身的、可选择分布式的多构架(amd64,arm64)蜜罐平台,支持20多个蜜罐和很多可视化选项,使用弹性堆栈、动画实时攻击地图和许多安全工具来进一步改善欺骗体验。GitHub - telekom-sec…

如何让 VSCode 认识你正在开发的 NPM 模块

假如你正在开发一个 NPM 模块 echox,并且在 src/index.js 里面导出了一系列方法: // ./src/index.js export function html() {// ... }然后在 tests/index.spec.js 里面新增了以下一行: // ./tests/index.spec.js import * as X from echox;如何让 VS…

【面试干货】SQL中count(*)、count(1)和count(column)的区别与用法

【面试干货】SQL中count(*)、count(1)和count(column)的区别与用法 1、count(*)2、count(1)3、count(column) 💖The Begin💖点点关注,收藏不迷路💖 在SQL中&a…

【Qt 学习笔记】Qt窗口 | 对话框 | Qt对话框的分类及介绍

博客主页:Duck Bro 博客主页系列专栏:Qt 专栏关注博主,后期持续更新系列文章如果有错误感谢请大家批评指出,及时修改感谢大家点赞👍收藏⭐评论✍ Qt窗口 | 对话框 | 模态对话框 文章编号:Qt 学习笔记 / 51…

【成品设计】基于STM32的智能厨房环境监测与报警系统

《基于STM32的智能厨房环境监测与报警系统》 所需器件: stm32f103c8t6烟雾气体传感器可燃性气体传感器温度传感器语音播放器模块和LED灯进行语音声光播报WIFI模块进行远程播报OLED 屏幕来显示火灾信息 整体功能: 通过温湿度传感器、烟雾气体传感器、…

数据可视化之常用图表热力图

1.什么是热力图? 热力图,是一种通过对色块着色来显示数据的统计图表。 绘图时,需指定颜色映射的规则。 例如,较大的值由较深的颜色表示,较小的值由较浅的颜色表示;较大的值由偏暖的颜色表示,…

搭建Vulnhub靶机网络问题(获取不到IP)

搭建好靶场后,在攻击机运行arp-scan -l无法发现靶机IP。 这时候去看下靶机网络有没有问题。 重新启动客户机,一直按e进入安全模式(要是直接开机了就先按shift进入grub界面,再按e)找到ro,将ro改为rw signie…

SELinux深度解析:安全增强型Linux的探索与应用(下)

🐇明明跟你说过:个人主页 🏅个人专栏:《Linux :从菜鸟到飞鸟的逆袭》🏅 🔖行路有良友,便是天堂🔖 目录 一、SELinux的工作机制 1、SELinux的三种状态:Pe…

SpringSecurity6从入门到实战之默认用户的生成流程

SpringSecurity6从入门到实战之默认用户的生成流程 这次还是如标题所示,上一章我们的登录页面已经知道是如何生成了.那么,我们通过表单登录的user用户以及密码SpringSecurity是如何进行生成的呢? 默认用户生成 让我们把登录流程重新拉回到读取/META-INF/spring/ .imports文件 …

Python用于简化数据操作和分析工作库之DaPy使用详解

概要 在数据科学和机器学习领域,处理和分析数据是关键的一步。Python 的 DaPy 库提供了一组强大的工具,用于简化数据操作和分析工作。DaPy 旨在提供高效且直观的 API,使得数据处理变得更加便捷。本文将详细介绍 DaPy 库,包括其安装方法、主要特性、基本和高级功能,以及实…

Python3 迭代器和生成器

前言 本文主要介绍Python中的迭代器和生成器,主要内容包括 迭代器概述、生成器简介。 文章目录 前言一、迭代器简介二、生成器简介 一、迭代器简介 在 Python 中,迭代器(iterator)是一个实现了迭代器协议(Iterator Protocol)的…

【JavaEE进阶】——MyBatis操作数据库 (#{}与${} 以及 动态SQL)

目录 🚩#{}和${} 🎈#{} 和 ${}区别 🎈${}使用场景 📝排序功能 📝like 查询 🚩数据库连接池 🎈数据库连接池使⽤ 🚩MySQL开发企业规范 🚩动态sql &#x1f388…

【Python】 探索Python中的目录遍历:获取当前目录下所有子目录列表

基本原理 在Python中,处理文件和目录是一项常见的任务,尤其是在进行文件管理、数据备份或自动化脚本时。Python的os模块提供了丰富的功能来与操作系统进行交互,包括文件和目录的遍历。要获取当前目录下的所有子目录,我们可以使用…

3年前端期望18K,云账户社招一面

一二面会有手写代码测试,一面或者二面当中,有一面必须到现场来的,对工作环境有一个直观的感受,前端二面取消了 一面(通过) 1、自我介绍、项目经历 2、怎么跟 xx模板的开发同学去沟通的呢?此处…

C++标准模板(STL)- 迭代器库-迭代器适配器- 逆序遍历的迭代器适配器 (二)

迭代器库-迭代器原语 迭代器库提供了五种迭代器的定义,同时还提供了迭代器特征、适配器及相关的工具函数。 迭代器分类 迭代器共有五 (C17 前)六 (C17 起)种:遗留输入迭代器 (LegacyInputIterator) 、遗留输出迭代器 (LegacyOutputIterator) 、遗留向前…

基于阿里云服务网格流量泳道的全链路流量管理(三):无侵入式的宽松模式泳道

作者:尹航 在前文《基于阿里云服务网格流量泳道的全链路流量管理(一):严格模式流量泳道》、《基于阿里云服务网格流量泳道的全链路流量管理(二):宽松模式流量泳道》中,我们介绍了流…

【Python系列】Python 方法变量参数详解

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

windows RNDIS开发-概念

远程 NDIS (RNDIS) 是一种独立于总线的类,适用于动态 即插即用 (PnP) 总线(例如 USB、1394、蓝牙和 InfiniBand)上的以太网 (802.3) 网络设备。 远程 NDIS 通过抽象控制和数据通道在主计算机与远程 NDIS 设备之间定义与总线无关的消息协议。 …

实践记录-docker-step6-7/10-参考docker官网步骤操作记录-绑定挂载-多容器

参考来源: (应用的容器化实践)docker官方入门指南 https://docs.docker.com/get-started/ 本指南包含有关如何开始使用 Docker 的分步说明。本指南介绍如何: 将映像作为容器生成并运行。 使用 Docker Hub 共享映像。 使用带有数据…