Swift Combine — zip和combineLatest的理解与使用

2024-06-22 15:28

本文主要是介绍Swift Combine — zip和combineLatest的理解与使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Publisher 上还有一些其他的操作,比如 zipcombineLatest,能让我们在时序上对控制多个 Publisher 的结果进行类似 andor 的合并,它们在构建复杂 Publisher 逻辑时也十分有用。

zip

Publisher 中的 zipSequencezip 相类似:它会把两个 (或多个) Publisher 事件序列中在同一 index 位置上的值进行合并,也就是说,Publisher1 中的第一个事件和 Publisher2 中的第一个事件结对合并,Publisher1 中的第二个事件和 Publisher2 中的第二个事件合并,以此类推:
在这里插入图片描述
比如上图中:
Publisher1 中第一个位置值为 1,在等待到 Publisher2 的第一个值 A 到来时,两者进行合并,在输出的 Publisher 中形成 (1, A)。
两个 Publisher 第二个位置的 2 和 B 也同理。
在 Publisher2 中 C 和 D 连续发生时,由于 Publisher1 里对应序列位置的事件还没有发生,因此并不会输出在最终的 Publisher 中,直到 Publisher1 里的 3 和 4 被发布。
最后 5 在 Publisher2 里并没有对应位置的 Output 事件,因此它被最终的 Publisher 忽略了。

下面的代码全完验证了上面的结论:

import SwiftUI
import Combineclass ZipViewModel: ObservableObject {private var cancellable = Set<AnyCancellable>()let numbersPub = PassthroughSubject<Int, Never>()let lettersPub = PassthroughSubject<String, Never>()@Published var receivedValues: [(Int, String)] = []func zipSample() {numbersPub.zip(lettersPub).sink(receiveValue: { value inprint("Received value: \(value)")self.receivedValues.append(value)}).store(in: &cancellable)}func sendNumber(_ number: Int) {print("Send number: \(number)")numbersPub.send(number)}func sendLetter(_ letter: String) {print("Send letter: \(letter)")lettersPub.send(letter)}
}struct ZipDemo: View {@StateObject private var viewModel = ZipViewModel()@State private var number: String = ""@State private var letter: String = ""var body: some View {VStack {VStack {TextField("Enter number...", text: $number)TextField("Enter letter...", text: $letter)}.textFieldStyle(.roundedBorder).padding()HStack {Button("Send number") {if let intNumber = Int(number) {viewModel.sendNumber(intNumber)}}.frame(maxWidth: .infinity)Button("Send letter") {if !letter.isEmpty {viewModel.sendLetter(letter)}}.frame(maxWidth: .infinity)}.buttonStyle(BorderedProminentButtonStyle())}.onAppear {viewModel.zipSample()}}
}

打印结果如下:

Send number: 1
Send letter: A
Received value: (1, "A")
Send number: 2
Send letter: B
Received value: (2, "B")
Send letter: C
Send letter: D
Send number: 3
Received value: (3, "C")
Send number: 4
Received value: (4, "D")
Send number: 5

在这里插入图片描述
zip 在时序语义上更接近于“当…且…”,当 Publisher1 发布值,且 Publisher2 发布值时,将两个值合并,作为新的事件发布出去。在实践中,zip 经常被用在合并多个异步事件的结果,比如同时发出了多个网络请求,希望在它们全部完成的时候把结果合并在一起。

combineLatest

zip 相反,combineLatest的语义接近于“当…或…”,当 Publisher1 发布值,或者 Publisher2 发布值时,将两个值合并,作为新的事件发布出去。
不论是哪个 Publisher,只要发生了新的事件,combineLatest 就把新发生的事件值和另一个 Publisher 中当前的最新值合并。

combineLatest 操作的图示如下:
在这里插入图片描述
上图中,除了首个元素 1 以外,其余的每个事件值,不论是从哪个 Publisher 发出的,都会被用来进行组合和输出。
比如在 Publisher2 输出 A 时,combineLatest 将使用这个 A 和 Publisher1 的当前最终值1 组合出结果1A。同样,随后 Publisher1 发布的 2 也和 Publisher2 的最终值 A 组合得到 2A。这个过程持续下去,直到两个 Publisher 都结束。

下面的代码验证了这一结论:

import SwiftUI
import Combineclass CombineLatestViewModel: ObservableObject {private var cancellable = Set<AnyCancellable>()let numbersPub = PassthroughSubject<Int, Never>()let lettersPub = PassthroughSubject<String, Never>()@Published var receivedValues: [(Int, String)] = []func zipSample() {numbersPub.combineLatest(lettersPub).sink(receiveValue: { value inprint("Received value: \(value)")self.receivedValues.append(value)}).store(in: &cancellable)}func sendNumber(_ number: Int) {print("Send number: \(number)")numbersPub.send(number)}func sendLetter(_ letter: String) {print("Send letter: \(letter)")lettersPub.send(letter)}
}struct CombineLatestDemo: View {@StateObject private var viewModel = CombineLatestViewModel()@State private var number: String = ""@State private var letter: String = ""var body: some View {VStack {VStack {TextField("Enter number...", text: $number)TextField("Enter letter...", text: $letter)}.textFieldStyle(.roundedBorder).padding()HStack {Button("Send number") {if let intNumber = Int(number) {viewModel.sendNumber(intNumber)}}.frame(maxWidth: .infinity)Button("Send letter") {if !letter.isEmpty {viewModel.sendLetter(letter)}}.frame(maxWidth: .infinity)}.buttonStyle(BorderedProminentButtonStyle())}.onAppear {viewModel.zipSample()}}
}

打印结果为:

Send number: 1
Send letter: A
Received value: (1, "A")
Send number: 2
Received value: (2, "A")
Send letter: B
Received value: (2, "B")
Send letter: C
Received value: (2, "C")
Send letter: D
Received value: (2, "D")
Send number: 3
Received value: (3, "D")
Send number: 4
Received value: (4, "D")
Send number: 5
Received value: (5, "D")

在这里插入图片描述

在实践中,combineLatest 被用来处理多个可变状态,在其中某一个状态发生变化时,获取这些全部状态的最新值。比如你的 UI 上有多个 TextField,你可能想要在其中某一个值变动时获取到所有 TextField 中的值并对它们进行检查,比如说用户注册界面。

写在最后

对于 zipcombineLatest,它们有一个共同特点,那就是结合后的新 Publisher 所发出的数据是元组类型。对这两种操作,一种常见的模式是将结果的发出多元组数据的 Publisher 沿着响应链继续传递,使用我们之前看到过的各类 Operator 来获取能实际驱动 UI 和 app 状态的 Publisher

最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

这篇关于Swift Combine — zip和combineLatest的理解与使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1084671

相关文章

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

LiteFlow轻量级工作流引擎使用示例详解

《LiteFlow轻量级工作流引擎使用示例详解》:本文主要介绍LiteFlow是一个灵活、简洁且轻量的工作流引擎,适合用于中小型项目和微服务架构中的流程编排,本文给大家介绍LiteFlow轻量级工... 目录1. LiteFlow 主要特点2. 工作流定义方式3. LiteFlow 流程示例4. LiteF

使用Python开发一个现代化屏幕取色器

《使用Python开发一个现代化屏幕取色器》在UI设计、网页开发等场景中,颜色拾取是高频需求,:本文主要介绍如何使用Python开发一个现代化屏幕取色器,有需要的小伙伴可以参考一下... 目录一、项目概述二、核心功能解析2.1 实时颜色追踪2.2 智能颜色显示三、效果展示四、实现步骤详解4.1 环境配置4.

使用jenv工具管理多个JDK版本的方法步骤

《使用jenv工具管理多个JDK版本的方法步骤》jenv是一个开源的Java环境管理工具,旨在帮助开发者在同一台机器上轻松管理和切换多个Java版本,:本文主要介绍使用jenv工具管理多个JD... 目录一、jenv到底是干啥的?二、jenv的核心功能(一)管理多个Java版本(二)支持插件扩展(三)环境隔

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用