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实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

redis中使用lua脚本的原理与基本使用详解

《redis中使用lua脚本的原理与基本使用详解》在Redis中使用Lua脚本可以实现原子性操作、减少网络开销以及提高执行效率,下面小编就来和大家详细介绍一下在redis中使用lua脚本的原理... 目录Redis 执行 Lua 脚本的原理基本使用方法使用EVAL命令执行 Lua 脚本使用EVALSHA命令

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

使用Python和Pyecharts创建交互式地图

《使用Python和Pyecharts创建交互式地图》在数据可视化领域,创建交互式地图是一种强大的方式,可以使受众能够以引人入胜且信息丰富的方式探索地理数据,下面我们看看如何使用Python和Pyec... 目录简介Pyecharts 简介创建上海地图代码说明运行结果总结简介在数据可视化领域,创建交互式地

Java Stream流使用案例深入详解

《JavaStream流使用案例深入详解》:本文主要介绍JavaStream流使用案例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录前言1. Lambda1.1 语法1.2 没参数只有一条语句或者多条语句1.3 一个参数只有一条语句或者多

Java Spring 中 @PostConstruct 注解使用原理及常见场景

《JavaSpring中@PostConstruct注解使用原理及常见场景》在JavaSpring中,@PostConstruct注解是一个非常实用的功能,它允许开发者在Spring容器完全初... 目录一、@PostConstruct 注解概述二、@PostConstruct 注解的基本使用2.1 基本代

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

springboot使用Scheduling实现动态增删启停定时任务教程

《springboot使用Scheduling实现动态增删启停定时任务教程》:本文主要介绍springboot使用Scheduling实现动态增删启停定时任务教程,具有很好的参考价值,希望对大家有... 目录1、配置定时任务需要的线程池2、创建ScheduledFuture的包装类3、注册定时任务,增加、删

使用Python实现矢量路径的压缩、解压与可视化

《使用Python实现矢量路径的压缩、解压与可视化》在图形设计和Web开发中,矢量路径数据的高效存储与传输至关重要,本文将通过一个Python示例,展示如何将复杂的矢量路径命令序列压缩为JSON格式,... 目录引言核心功能概述1. 路径命令解析2. 路径数据压缩3. 路径数据解压4. 可视化代码实现详解1