Swift Combine 发布者订阅者操作者 从入门到精通二
Combine 系列
- Swift Combine 从入门到精通一
1. Combine核心概念
你只需要了解几个核心概念,就能使用好 Combine,但理解它们非常重要。 这些概念中的每一个都通过通用协议反映在框架中,以将概念转化为预期的功能。
这些核心概念是:
- Publisher and Subscriber
- Operator 操作符
- Subjects
2. Publisher and Subscriber
两个关键概念, publisher 和 subscriber,在 Swift 中被描述为协议。
当你谈论编程(尤其是 Swift 和 Combine)时,很多都使用类型描述。 当你说一个函数或方法返回一个值时,该值通常被描述为“此类型之一”。
Combine 就是定义随着时间的推移使用许多可能的值进行操作的过程。 Combine 还不仅仅是定义结果,它还定义了我们如何处理失败。 它不仅讨论可以返回的类型,还讨论可能发生的失败。
2.1 Publisher 发布者
现在我们要引入的第一个核心概念是发布者。 当其被订阅之后,根据请求会提供数据, 没有任何订阅请求的发布者不会提供任何数据。 当你描述一个 Combine 的发布者时,应该用两种相关的类型来描述它:一种用于输出,一种用于失败。

这些通常使用泛型语法编写,该语法在描述类型的文本周围使用 < 和 > 符号。 这表示我们正在谈论这种类型的值的通用实例。 例如,如果发布者返回了一个 String 类型的实例,并且可能以 URLError 实例的形式返回失败,那么发布者可能会用 <String, URLError> 来描述。
2.2 订阅者 Subscriber
与发布者匹配的对应概念是订阅者,是第二个要介绍的核心概念。
订阅者负责请求数据并接受发布者提供的数据(和可能的失败)。 订阅者同样被描述为两种关联类型,一种用于输入,一种用于失败。 订阅者发起数据请求,并控制它接收的数据量。 它可以被认为是在 Combine 中起“驱动作用”的,因为如果没有订阅者,其他组件将保持闲置状态,没有数据会流动起来。
发布者和订阅者是相互连接的,它们构成了 Combine 的核心。 当你将订阅者连接到发布者时,两种类型都必须匹配:发布者的输出和订阅者的输入以及它们的失败类型。 将其可视化的一种方法是对两种类型进行一系列并行操作,其中两种类型都需要匹配才能将组件插入在一起。

2.3 操作符 Operator
第三个核心概念是操作符——一个既像订阅者又像发布者的对象。 操作符是同时实现了 订阅者协议 和 发布者协议 的类。 它们支持订阅发布者,并将结果发送给任何订阅者。
你可以用这些创建成链,用于处理和转换发布者提供的数据和订阅者请求的数据。
我称这些组合序列为管道。

操作符可用于转换值或类型 - 输出和失败类型都可以。 操作符还可以拆分或复制流,或将流合并在一起。 操作符必须始终按输出/失败这样的类型组合对齐。 编译器将强制执行匹配类型,因此类型错误将导致编译器错误(如果幸运的话,会有一个有用的 fixit 片段建议给你解决方案)。
用 swift 编写的简单的 Combine 管道如下所示:
let _ = Just(5) .map { value -> String in // do something with the incoming value here// and return a stringreturn "a string"}.sink { receivedValue in // sink is the subscriber and terminates the pipelineprint("The end result was \(receivedValue)")}
- 管道从发布者
Just开始,它用它定义的值(在本例中为整数 5)进行响应。输出类型为<Integer>,失败类型为<Never>。 - 然后管道有一个
map操作符,它在转换值及其类型。 在此示例中,它忽略了发布者发出的输入并返回了一个字符串。 这也将输出类型转换为<String>,并将失败类型仍然保持为<Never>。 - 然后管道以
sink订阅者结束。
当你去尝试理解管道时,你可以将其视为由输出和失败类型链接的一系列操作。 当你开始构建自己的管道时,这种模式就会派上用场。 创建管道时,你可以选择操作符来帮助你转换数据、类型或两者同时使用以实现最终目的。 最终目标可能是启用或禁用用户界面的某个元素,或者可能是得到某些数据用来显示。 许多 Combine 的操作符专门设计用来做这些转换。
有许多操作符是以 try 为前缀的,这表示它们返回一个 <Error> 的失败类型。 例如 map 和 tryMap。 map 操作符可以转换输出和失败类型的任意组合。 tryMap 接受任何输入和失败类型,并允许输出任何类型,但始终会输出 <Error> 的失败类型。
像 map 这样的操作符,你在定义返回的输出类型时,允许你基于提供给操作符的闭包中返回的内容推断输出类型。 在上面的例子中,map 操作符返回一个 String 的输出类型,因为这正是闭包返回的类型。
为了更具体地说明更改类型的示例,我们扩展了值在传输过程中的转换逻辑。此示例仍然以提供类型 <Int, Never> 的发布者开始,并以类型为 <String, Never> 的订阅结束。
SwiftUI-NotesTests/CombinePatternTests.swift
let _ = Just(5) .map { value -> String in switch value {case _ where value < 1:return "none"case _ where value == 1:return "one"case _ where value == 2:return "couple"case _ where value == 3:return "few"case _ where value > 8:return "many"default:return "some"}}.sink { receivedValue in print("The end result was \(receivedValue)")}
Just是创建一个<Int, Never>类型组合的发布者,提供单个值然后完成。- 提供给
.map()函数的闭包接受一个<Int>并将其转换为一个<String>。由于<Never>的失败类型没有被改变,所以就直接输出了。 sink作为订阅者,接受<String, Never>类型的组合数据。
当你在 Xcode 中创建管道,类型不匹配时,Xcode 中的错误消息可能包含一个有用的修复建议 fixit。 在某些情况下,例如上个例子,当提供给 map 的闭包中不指定特定的返回类型时,编译器就无法推断其返回值类型。 Xcode (11 beta 2 and beta 3) 显示此为错误消息:
Unable to infer complex closure return type; add explicit type to disambiguate。 在上面示例中,我们用value → String in明确指定了返回的类型。
你可以将 Combine 的发布者、操作符和订阅者视为具有两种需要对齐的平行类型 —— 一种用于成功的有用值,另一种用于错误处理。 设计管道时经常会选择如何转换其中一种或两种类型以及与之相关的数据。
参考
https://heckj.github.io/swiftui-notes/index_zh-CN.html
代码
https://github.com/heckj/swiftui-notes
相关文章:
Swift Combine 发布者订阅者操作者 从入门到精通二
Combine 系列 Swift Combine 从入门到精通一 1. Combine核心概念 你只需要了解几个核心概念,就能使用好 Combine,但理解它们非常重要。 这些概念中的每一个都通过通用协议反映在框架中,以将概念转化为预期的功能。 这些核心概念是&#x…...
python 笔记:shapely(形状篇)
主要是点(point)、线(linestring)、面(surface) 1 基本方法和属性 object.area 返回对象的面积(浮点数) object.bounds 返回一个(minx, miny, maxx, maxy)元…...
开源的JS动画框架库介绍
开源的JS动画框架库介绍 在现代网页设计中,动画已经成为提升用户体验的重要手段。它们不仅能够吸引用户的注意力,还能够帮助用户更好地理解和导航网站。JavaScript 动画框架库提供了一套丰富的动画效果,让开发者能够轻松地实现复杂的…...
MATLAB实现随机森林回归算法
随机森林回归是一种基于集成学习的机器学习算法,它通过组合多个决策树来进行回归任务。随机森林的基本思想是通过构建多个决策树,并将它们的预测结果进行平均或投票来提高模型的准确性和鲁棒性。 以下是随机森林回归的主要特点和步骤: 决策树…...
时间序列预测——BiGRU模型
时间序列预测——BiGRU模型 时间序列预测是指根据历史数据的模式来预测未来时间点的值或趋势的过程。在深度学习领域,循环神经网络(Recurrent Neural Networks, RNNs)是常用于时间序列预测的模型之一。在RNNs的基础上,GRU&#x…...
django中实现数据库操作
在Django中,数据库操作通常通过Django的ORM(Object-Relational Mapping)来实现。ORM允许你使用Python类来表示数据库表,并可以使用Python语法来查询和操作数据库。 以下是在Django中实现数据库操作的基本步骤: 一&am…...
使用 FFmpeg 将视频转换为 GIF 动画的技巧
使用 FFmpeg 将视频转换为 GIF 动画 FFmpeg 可以将视频转换为 GIF 动画,方法如下: 1. 准备工作 确保您已经安装了 FFmpeg。 熟悉 FFmpeg 的命令行使用。 了解 GIF 动画的基本知识。 2. 基本命令 ffmpeg -i input.mp4 output.gif 3. 参数说明 -i in…...
2024春晚纸牌魔术原理----环形链表的约瑟夫问题
一.题目及剖析 https://www.nowcoder.com/practice/41c399fdb6004b31a6cbb047c641ed8a?tabnote 这道题涉及到数学原理,有一般公式,但我们先不用公式,看看如何用链表模拟出这一过程 二.思路引入 思路很简单,就试创建一个单向循环链表,然后模拟报数,删去对应的节点 三.代码引…...
HCIA-HarmonyOS设备开发认证V2.0-轻量系统内核内存管理-静态内存
目录 一、内存管理二、静态内存2.1、静态内存运行机制2.2、静态内存开发流程2.3、静态内存接口2.4、实例2.5、代码分析(待续...)坚持就有收货 一、内存管理 内存管理模块管理系统的内存资源,它是操作系统的核心模块之一,主要包括…...
什么是vite,如何使用
参考: 主要:由一次业务项目落地 Vite 的经历,我重新理解了 Vite 预构建 vite官方文档 为什么有人说 vite 快,有人却说 vite 慢? 深入理解Vite核心原理 面向未来的前端构建工具-vite 聊一聊 Vite 的预构建和二次预构建 …...
基于大语言模型的AI Agents
代理(Agent)指能自主感知环境并采取行动实现目标的智能体。基于大语言模型(LLM)的 AI Agent 利用 LLM 进行记忆检索、决策推理和行动顺序选择等,把Agent的智能程度提升到了新的高度。LLM驱动的Agent具体是怎么做的呢&a…...
23种设计模式之抽象工厂模式
目录 什么是抽象工厂模式 基本结构 基本实现步骤 实现代码(有注释) 应用场景 简单工厂、工厂方法、抽象工厂的区别 什么是抽象工厂模式 抽象工厂模式也是一种创建型设计模式,提供了一系列相关或相互依赖对象的接口,而无需…...
飞天使-linux操作的一些技巧与知识点9-zabbix6.0 容器之纸飞机告警设置
文章目录 zabbix 告警纸飞机方式webhook 方式 告警设置 zabbix 告警纸飞机方式 第一种方式参考 https://blog.csdn.net/yetugeng/article/details/99682432bash-4.4$ cat telegram.sh #!/bin/bashMSG$1TOKEN"61231432278:AAsdfsdfsdfsdHUxBwPSINc2kfOGhVik" CHAT_I…...
京东组件移动端库的使用 Nut-UI
1.介绍 NutUI NutUI-Vue 组件库,基于 Taro,使用 Vue 技术栈开发小程序应用,开箱即用,帮助研发快速开发用户界面,提升开发效率,改善开发体验。 特性 🚀 80 高质量组件,覆盖移动端主…...
用Python来实现2024年春晚刘谦魔术
简介 这是新春的第一篇,今天早上睡到了自然醒,打开手机刷视频就被刘谦的魔术所吸引,忍不住用编程去模拟一下这个过程。 首先,声明的一点,大年初一不学习,所以这其中涉及的数学原理约瑟夫环大家可以找找其…...
TestNG基础教程
TestNG基础教程 一、常用断言二、执行顺序三、依赖测试四、参数化测试1、通过dataProvider实现2、通过xml配置(这里是直接跑xml) 五、testng.xml常用配置方式1、分组维度控制2、类维度配置3、包维度配置 六、TestNG并发测试1、通过注解来实现2、通过xml来…...
###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
前言:感谢您的关注哦,我会持续更新编程相关知识,愿您在这里有所收获。如果有任何问题,欢迎沟通交流!期待与您在学习编程的道路上共同进步。 一. 两个主要软件的介绍 1.KeiluVision5软件 Keil uVision5是一款集成开发…...
Android 9.0 任务栏中清除掉播放器的进程,状态栏仍有音乐播放器状态问题的解决
1.概述 在9.0的rom定制化开发中,在点击系统自带的播放器以后,播放音乐的时候,在最近任务栏recents列表中,点击全部清除,发现音乐播放器还在播放音乐,导致出现bug,完整的 解决方法,肯定是需要点击全部清除以后,音乐播放器也被杀掉进程,接下来分析下这个移除任务栏流程…...
【笔记】Helm-5 Chart模板指南-13 调是模版
调试模板 调试模板可能很棘手,因为渲染后的模板发送了kubernetes API server,可能会以格式化以外的原因拒绝YAML文件。 以下命令有助于调试: 1、helm lint 是验证chart是否遵循最佳实践的首选工具。 2、helm template --debug在本地测试渲…...
Gateway反向代理配置
前言 一般而言,反向代理都是在Nginx中来实现的,其实Gateway也可以作为反向代理服务,不过一般不会这么做,只不过最近的项目,在通过Nginx反向代理之后,iPhone手机访问接口代理地址会异常,安卓手机…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
《Offer来了:Java面试核心知识点精讲》大纲
文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...
归并排序:分治思想的高效排序
目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法,由约翰冯诺伊曼在1945年提出。其核心思想包括: 分割(Divide):将待排序数组递归地分成两个子…...
高保真组件库:开关
一:制作关状态 拖入一个矩形作为关闭的底色:44 x 22,填充灰色CCCCCC,圆角23,边框宽度0,文本为”关“,右对齐,边距2,2,6,2,文本颜色白色FFFFFF。 拖拽一个椭圆,尺寸18 x 18,边框为0。3. 全选转为动态面板状态1命名为”关“。 二:制作开状态 复制关状态并命名为”开…...
基于小程序老人监护管理系统源码数据库文档
摘 要 近年来,随着我国人口老龄化问题日益严重,独居和居住养老机构的的老年人数量越来越多。而随着老年人数量的逐步增长,随之而来的是日益突出的老年人问题,尤其是老年人的健康问题,尤其是老年人产生健康问题后&…...
比较数据迁移后MySQL数据库和ClickHouse数据仓库中的表
设计一个MySQL数据库和Clickhouse数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
