Combine 框架中的运算符
Combine 提供了一系列强大的运算符来处理数据流,以下是一些常用的运算符及其含义和操作
转换运算符
-
map
将发布者产生的每个值映射为另一个值。
import Combine
let numbers = [1, 2, 3, 4, 5]
let publisher = numbers.publisher
let cancellable = publisher
.map { $0 * 2 }
.sink(receiveValue: { value in
print("Mapped value: (value)")
})
// 输出:
// Mapped value: 2
// Mapped value: 4
// Mapped value: 6
// Mapped value: 8
// Mapped value: 10
-
flatMap
将发布者产生的值转换为新的发布者,并扁平化处理。
import Combine
let numbers = [1, 2, 3, 4, 5]
let publisher = numbers.publisher
let cancellable = publisher
.flatMap { number in
return Just(number * 2)
}
.sink(receiveValue: { value in
print("FlatMapped value: (value)")
})
// 输出:
// FlatMapped value: 2
// FlatMapped value: 4
// FlatMapped value: 6
// FlatMapped value: 8
// FlatMapped value: 10
-
compactMap
与 map
类似,但会过滤掉转换后为 nil
的值。
import Combine
let numbers = [1, 2, 3, 4, 5]
let publisher = numbers.publisher
let cancellable = publisher
.compactMap { number -> Int? in
return number % 2 == 0 ? number : nil
}
.sink(receiveValue: { value in
print("CompactMapped value: (value)")
})
// 输出:
// CompactMapped value: 2
// CompactMapped value: 4
-
allSatisfy
检查发布者的所有输出是否满足指定条件。
let publisher = [1, 2, 3, 4, 5].publisher
let cancellable = publisher.allSatisfy { $0 > 0 }
.sink { print("All greater than 0: ($0)") }
// 输出: All greater than 0: true
过滤运算符
-
filter
只允许通过满足特定条件的值。
import Combine
let numbers = [1, 2, 3, 4, 5]
let publisher = numbers.publisher
let cancellable = publisher
.filter { $0 % 2 == 0 }
.sink(receiveValue: { value in
print("Filtered value: (value)")
})
// 输出:
// Filtered value: 2
// Filtered value: 4
-
removeDuplicates
移除连续重复的值。
import Combine
let numbers = [1, 2, 2, 3, 3, 3, 4, 5]
let publisher = numbers.publisher
let cancellable = publisher
.removeDuplicates()
.sink(receiveValue: { value in
print("Unique value: (value)")
})
// 输出:
// Unique value: 1
// Unique value: 2
// Unique value: 3
// Unique value: 4
// Unique value: 5
-
dropFirst
跳过前 n 个值。
import Combine
let numbers = [1, 2, 3, 4, 5]
let publisher = numbers.publisher
let cancellable = publisher
.dropFirst(3)
.sink(receiveValue: { value in
print("Value after dropFirst: (value)")
})
// 输出:
// Value after dropFirst: 4
// Value after dropFirst: 5
-
dropWhile
当条件为真时,跳过值。
import Combine
let numbers = [1, 2, 3, 4, 5]
let publisher = numbers.publisher
let cancellable = publisher
.dropWhile { $0 < 3 }
.sink(receiveValue: { value in
print("Value after dropWhile: (value)")
})
// 输出:
// Value after dropWhile: 3
// Value after dropWhile: 4
// Value after dropWhile: 5
-
prefix
只取前 n 个值。
import Combine
let numbers = [1, 2, 3, 4, 5]
let publisher = numbers.publisher
let cancellable = publisher
.prefix(3)
.sink(receiveValue: { value in
print("Value with prefix: (value)")
})
// 输出:
// Value with prefix: 1
// Value with prefix: 2
// Value with prefix: 3
-
ignoreOutput
ignoreOutput
是一个操作符,用于忽略发布者发出的所有元素,仅关注完成事件或错误事件的发生。它在某些情况下非常有用,例如当我们只关心发布者是否成功完成,而不关心它发出的具体值时使用。
以下是一个简单的示例,展示了如何使用 ignoreOutput
操作符来忽略发布者发出的元素,只关注完成事件的发生。
import Combine
// 创建一个简单的发布者,每隔一秒发出一个整数,共发出三个元素
let publisher = Timer.publish(every: 1, on: .main, in: .default)
.autoconnect()
.prefix(3) // 限制只发出三个元素,用于示例简化
// 创建了一个 sink 订阅者,用于接收和打印发布者发出的每一个值。
let cancellable = publisher
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Publisher finished successfully")
case .failure(let error):
print("Publisher failed with error:", error)
}
}, receiveValue: { value in
print("Received value:", value)
})
let ignoreCancellable = publisher
// 使用ignoreOutput 操作符创建了一个新的订阅者,它会忽略发布者发出的所有元素,仅关注完成事件。
.ignoreOutput()
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Ignore Output: Publisher finished successfully")
case .failure(let error):
print("Ignore Output: Publisher failed with error:", error)
}
}, receiveValue: { value in
print("Received ignore value:", value) // 这里会收到xcode的⚠️:Will never be executed
})
// 取消订阅
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
cancellable.cancel()
ignoreCancellable.cancel()
}
// 输出结果
// Received value: 0
// Received value: 1
// Received value: 2
// Publisher finished successfully
// Ignore Output: Publisher finished successfully
组合运算符
-
merge
将多个发布者合并为一个。
import Combine
let publisher1 = [1, 2, 3].publisher
let publisher2 = [4, 5, 6].publisher
let mergedPublisher = Publishers.Merge(publisher1, publisher2)
let cancellable = mergedPublisher
.sink(receiveValue: { value in
print("Merged value: (value)")
})
// 输出:
// Merged value: 1
// Merged value: 2
// Merged value: 3
// Merged value: 4
// Merged value: 5
// Merged value: 6
-
combineLatest
当任一发布者产生新值时,组合所有发布者的最新值。
import Combine
let publisher1 = PassthroughSubject<Int, Never>()
let publisher2 = PassthroughSubject<String, Never>()
let combinedPublisher = Publishers.CombineLatest(publisher1, publisher2)
let cancellable = combinedPublisher
.sink(receiveValue: { value1, value2 in
print("Combined latest values: (value1) and (value2)")
})
publisher1.send(1)
publisher2.send("A")
publisher1.send(2)
publisher2.send("B")
// 输出:
// Combined latest values: 1 and A
// Combined latest values: 2 and A
// Combined latest values: 2 and B
-
zip
当所有发布者都产生新值时,组合这些值。
import Combine
let publisher1 = PassthroughSubject<Int, Never>()
let publisher2 = PassthroughSubject<String, Never>()
let zippedPublisher = Publishers.Zip(publisher1, publisher2)
let cancellable = zippedPublisher
.sink(receiveValue: { value1, value2 in
print("Zipped values: (value1) and (value2)")
})
publisher1.send(1)
publisher2.send("A")
publisher1.send(2)
publisher2.send("B")
// 输出:
// Zipped values: 1 and A
// Zipped values: 2 and B
-
switchToLatest
switchToLatest
操作符用于处理嵌套的 Publisher 结构,它能够监视多个 Publishers,并在最新的 Publisher 发出新元素时切换到该 Publisher 的输出。这个操作符在处理一些需要动态切换订阅者的场景下非常有用,例如处理用户操作、网络请求等异步任务。
假设我们有一个需求,需要从不同的网络请求中获取数据,这些请求依赖于先前请求的结果。下面的示例将演示如何使用 switchToLatest
来处理这种情况。
import Combine
import Foundation
// 定义一个用于模拟网络请求的函数,返回一个 Future Publisher
func fetchDataPublisher(requestNumber: Int) -> AnyPublisher<String, Error> {
return Future<String, Error> { promise in
DispatchQueue.global().asyncAfter(deadline: .now() + Double(requestNumber)) {
promise(.success("Response from request (requestNumber)"))
}
}
.eraseToAnyPublisher()
}
// 创建一个序列,依次发起三个网络请求
let sequencePublisher = Publishers.Sequence(sequence: [1, 2, 3])
.map { requestNumber in
fetchDataPublisher(requestNumber: requestNumber)
}
.switchToLatest() // 切换到最新的 Publisher 输出
// 订阅并处理结果
let cancellable = sequencePublisher
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("All requests completed")
case .failure(let error):
print("Error:", error)
}
}, receiveValue: { value in
print("Received value:", value)
})
// 添加到集合中以确保不会被释放
var cancellables = Set<AnyCancellable>()
cancellable.store(in: &cancellables)
// 输出
// Received value: Response from request 3
// All requests completed
在这个示例中switchToLatest()
操作符在每次 map
产生的新 Publisher 发出新元素时,自动切换到最新的 Publisher 的输出。
- 当第一个请求(编号为1)的
fetchDataPublisher
返回其 Future Publisher 时,Combine 开始监听并等待其结果。 - 然后,当第二个请求(编号为2)的
fetchDataPublisher
返回其 Future Publisher 时,由于使用了switchToLatest()
,Combine 将取消之前的订阅(即第一个请求的 Publisher),开始等待第二个请求的结果。 - 最后,当第三个请求(编号为3)的
fetchDataPublisher
返回其 Future Publisher 时,Combine 又会取消前一个订阅(即第二个请求的 Publisher),开始等待第三个请求的结果。
因此,由于 switchToLatest()
操作符的特性,它只会处理最新的 Publisher 输出,也就是最后一个请求(编号为3)的结果。前两个请求的结果由于被后续请求的结果所取代,因此不会被打印出来。
-
collect
收集一组发布者的值,并将其作为数组发布。
let publisher = [1, 2, 3, 4, 5].publisher
let cancellable = publisher.collect()
.sink { print("Collected values: ($0)") }
// 输出: Collected values: [1, 2, 3, 4, 5]
序列运算符
-
scan
累积计算发布者产生的值,类似于 reduce
。
import Combine
let numbers = [1, 2, 3, 4, 5]
let publisher = numbers.publisher
let cancellable = publisher
.scan(0) { accumulated, value in
accumulated + value
}
.sink(receiveValue: { value in
print("Scanned value: (value)")
})
// 输出:
// Scanned value: 1
// Scanned value: 3
// Scanned value: 6
// Scanned value: 10
// Scanned value: 15
-
reduce
完成所有值后,进行一次性累积计算。
import Combine
let numbers = [1, 2, 3, 4, 5]
let publisher = numbers.publisher
let cancellable = publisher
.reduce(0) { accumulated, value in
accumulated + value
}
.sink(receiveValue: { value in
print("Reduced value: (value)")
})
// 输出:
// Reduced value: 15
-
prepend
在发布者之前插入一个值或一组值。
let publisher = [3, 4, 5].publisher
let cancellable = publisher.prepend(1, 2)
.sink { print("Prepended value: ($0)") }
// 输出: 1, 2, 3, 4, 5
时间运算符
-
debounce
在指定的时间间隔内,只取最后一个值。
import Combine
import Foundation
let subject = PassthroughSubject<String, Never>()
let cancellable = subject
.debounce(for: .seconds(1), scheduler: RunLoop.main)
.sink(receiveValue: { value in
print("Debounced value: (value)")
})
subject.send("A")
subject.send("B")
subject.send("C")
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
subject.send("D")
}
// 输出:
// Debounced value: C
// Debounced value: D
-
throttle
在指定的时间间隔内,只允许产生一个值。
import Combine
import Foundation
let subject = PassthroughSubject<String, Never>()
let cancellable = subject
.throttle(for: .seconds(1), scheduler: RunLoop.main, latest: true)
.sink(receiveValue: { value in
print("Throttled value: (value)")
})
subject.send("A")
subject.send("B")
subject.send("C")
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
subject.send("D")
}
// 输出:
// Throttled value: C
// Throttled value: D
首先,字符串 "A" , "B" 和 "C" 被发送到 subject
,但在 1 秒内它们都被发送了,并且设置了latest = true
,throttle
操作符只会接收最后一个值,即 "C"。
在延迟 1.5 秒后,节流操作会接收到 "D" 并打印。
-
delay
延迟发布者产生的值。
import Combine
import Foundation
let subject = PassthroughSubject<String, Never>()
let cancellable = subject
.delay(for: .seconds(1), scheduler: RunLoop.main)
.sink(receiveValue: { value in
print("Delayed value: (value)")
})
subject.send("A")
// 输出 (延迟1秒后):
// Delayed value: A
-
timeout
如果发布者未在指定时间内发出值,则发出超时错误。
let publisher = PassthroughSubject<Int, Never>()
let cancellable = publisher.timeout(.seconds(3), scheduler: DispatchQueue.main, customError: { URLError(.timedOut) })
.sink(receiveCompletion: { print("Completed with: ($0)") }, receiveValue: { print("Value: ($0)") })
// 输出: Completed with: failure(URLError)
-
measureInterval
measureInterval
是一个用于测量时间间隔的操作符。它可以帮助开发者监测两次元素发出之间的时间间隔,以及订阅者接收到元素之间的时间间隔。这对于性能监控、调试和数据流分析非常有用。
func measureInterval(using scheduler: Scheduler) -> Publishers.MeasureInterval<Self, Scheduler>
它接受一个调度器(Scheduler),返回一个 Publishers.MeasureInterval
类型的发布者。这个操作符会在每次接收到元素时记录当前时间,计算出与上次元素接收之间的时间间隔,并将间隔作为元素发出。
scheduler
: 用于计算时间间隔的调度器。通常情况下,可以使用.main
(主队列调度器)或者.immediate
(立即执行)等内置调度器。也可以传入自定义的调度器来控制时间间隔的计算行为。
主要使用场景有
- 监测数据流的速度和频率: 可以用来监测数据流中元素的发出速度,或者计算连续元素之间的时间间隔。
- 性能监控和调试: 可以用来分析和优化 Combine 数据流的性能,以及查找可能存在的延迟或者频率问题。
以下是一个简单的示例,展示了如何使用 measureInterval
操作符来测量元素发出之间的时间间隔。
import Combine
// 创建一个定时器发布者,每隔一秒发出一个整数
let publisher = Timer.publish(every: 1, on: .main, in: .default)
.autoconnect()
.prefix(5) // 限制只发出五个元素,用于示例简化
let cancellable = publisher
.measureInterval(using: DispatchQueue.main)
.sink(receiveCompletion: { completion in
print("Publisher finished with completion:", completion)
}, receiveValue: { interval in
print("Received interval:", interval)
})
// 输出结果
// Received interval: 1.0033320188522339
// Received interval: 1.0016639823913574
// Received interval: 1.0016380543708801
// Received interval: 1.0016300086975098
// Received interval: 1.0016199946403503
// Publisher finished with completion: finished
当然如果有时候我们希望将时间间隔和发布者的value同时输出,那么就需要通过flatmap将两者结合起来转化成新的publisher
import Combine
import Foundation
extension Date {
func customFormatted() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss"
let formattedTime = formatter.string(from: self)
return formattedTime
}
}
// 创建一个简单的发布者,每隔一秒发出一个整数
let publisher = Timer.publish(every: 1, on: .main, in: .default)
.autoconnect() // 自动连接
.prefix(5) // 限制只发出五个元素,用于示例简化
// 记录上一个值接收的时间
var lastReceivedTime: Date?
let cancellable = publisher
// 使用 flatMap 操作符来转换每个原始值和时间间隔的元组 (Int, TimeInterval)。
// 在 flatMap 的闭包中,我们计算了当前时间 currentTime,以及上一个值接收时间到当前时间的时间间隔 interval。
// 然后将值和时间间隔封装成一个元组,并通过 Just 创建一个新的发布者。
.flatMap { timestamp -> AnyPublisher<(value: String, interval: TimeInterval), Never> in
let currentTime = Date()
let interval: TimeInterval = lastReceivedTime.map { currentTime.timeIntervalSince($0) } ?? 0
lastReceivedTime = currentTime
return Just((timestamp.customFormatted(), interval)).eraseToAnyPublisher()
}
// 使用sink 订阅新的发布者发布的内容
.sink(receiveCompletion: { completion in
print("Publisher finished with completion:", completion)
}, receiveValue: { (value, interval) in
print("Received value:", value, ", Interval since last value:", interval)
})
// 取消订阅
DispatchQueue.main.asyncAfter(deadline: .now() + 6) {
cancellable.cancel()
}
// 输出结果
// Received value: 17:48:06 , Interval since last value: 0.0
// Received value: 17:48:07 , Interval since last value: 0.9998550415039062
// Received value: 17:48:08 , Interval since last value: 0.999998927116394
// Received value: 17:48:09 , Interval since last value: 0.9999690055847168
// Received value: 17:48:10 , Interval since last value: 0.9999220371246338
// Publisher finished with completion: finished
其他运算符
-
share
使发布者的多个订阅者共享一个订阅。
let publisher = Just("Hello, Combine!")
let sharedPublisher = publisher.share()
let cancellable1 = sharedPublisher.sink { print("Subscriber 1 received: ($0)") }
let cancellable2 = sharedPublisher.sink { print("Subscriber 2 received: ($0)") }
// 输出:
// Subscriber 1 received: Hello, Combine!
// Subscriber 2 received: Hello, Combine!
-
multicast
multicast
操作符允许你使用指定的 Subject
将单个发布者的输出分发给多个订阅者。这对于需要多个订阅者共享相同的事件流非常有用。
import Combine
// 使用 PassthroughSubject 创建发布者。
let publisher = PassthroughSubject<String, Never>()
// 使用 PassthroughSubject 创建一个 Subject。
let subject = PassthroughSubject<String, Never>()
// 使用 multicast 操作符将发布者与 Subject 关联,创建一个多播发布者。
let multicastedPublisher = publisher.multicast(subject: subject)
// 创建第一个订阅者,并使用 sink 订阅多播发布者。
let cancellable1 = multicastedPublisher
.sink(receiveCompletion: { completion in
print("Subscriber 1 completed with: (completion)")
}, receiveValue: { value in
print("Subscriber 1 received value: (value)")
})
// 创建第二个订阅者,并使用 sink 订阅多播发布者。
let cancellable2 = multicastedPublisher
.sink(receiveCompletion: { completion in
print("Subscriber 2 completed with: (completion)")
}, receiveValue: { value in
print("Subscriber 2 received value: (value)")
})
// 使用 connect 方法启动多播发布者,将事件传递给订阅者。
let connection = multicastedPublisher.connect()
// 通过 publisher.send 发送事件,所有订阅者会接收到相同的事件。
publisher.send("Hello")
publisher.send("Combine")
// 通过 publisher.send(completion: .finished) 完成发布者,所有订阅者会接收到完成事件。
publisher.send(completion: .finished)
// 通过 connection.cancel 断开多播连接。
connection.cancel()
// 输出结果
// Subscriber 1 received value: Hello
// Subscriber 2 received value: Hello
// Subscriber 1 received value: Combine
// Subscriber 2 received value: Combine
// Subscriber 1 completed with: finished
// Subscriber 2 completed with: finished
在这个例子中,multicast
确保了发布者的事件流被多个订阅者共享,所有订阅者都接收到了相同的事件。
-
buffer
buffer
操作符用于缓存发布者的输出,确保发布者不会因订阅者处理速度慢而阻塞。它允许你设置缓存的大小和策略(如提前取值和缓存已满时的处理方式)。
let erratic = Just("A").delay(for: 0.0, tolerance: 0.001, scheduler: DispatchQueue.main).eraseToAnyPublisher()
.merge(with: Just("B").delay(for: 0.1, tolerance: 0.001, scheduler: DispatchQueue.main).eraseToAnyPublisher())
.merge(with: Just("C").delay(for: 0.2, tolerance: 0.001, scheduler: DispatchQueue.main).eraseToAnyPublisher())
.merge(with: Just("D").delay(for: 0.3, tolerance: 0.001, scheduler: DispatchQueue.main).eraseToAnyPublisher())
.merge(with: Just("E").delay(for: 2, tolerance: 0.001, scheduler: DispatchQueue.main).eraseToAnyPublisher())
.merge(with: Just("F").delay(for: 5.0, tolerance: 0.001, scheduler: DispatchQueue.main).eraseToAnyPublisher())
.merge(with: Just("G").delay(for: 10, tolerance: 0.001, scheduler: DispatchQueue.main).eraseToAnyPublisher())
.merge(with: Just("H").delay(for: 20, tolerance: 0.001, scheduler: DispatchQueue.main).eraseToAnyPublisher())
.handleEvents(
receiveOutput: { print("erratic: (Date().hhmmss()) ($0)") },
receiveCompletion: { print("erratic: (Date().hhmmss()) ($0)") }
)
.makeConnectable()
let timerPublisher = Timer.publish(every: 1.0, on: .main, in: .common).autoconnect()
let bufferPublisher = erratic
.buffer(size: 3, prefetch: .byRequest, whenFull: .dropOldest)
.flatMap { values -> AnyPublisher<String, Never> in
if values.isEmpty {
print("Buffer is empty, no value published")
// Return an empty value to ensure the sink receives a value every second
return Just("").setFailureType(to: Never.self).eraseToAnyPublisher()
} else {
// Join all characters in the values sequence into a single String
let combinedString = values.map { String($0) }.joined()
return Just(combinedString).setFailureType(to: Never.self).eraseToAnyPublisher()
}
}
.makeConnectable()
let bufferSubscription = bufferPublisher
.sink(
receiveCompletion: { print("paced completion: (Date().hhmmss()) ($0)") },
receiveValue: { print("paced: (Date().hhmmss()) ($0)") }
)
.store(in: &subscribers)
erratic.connect().store(in: &subscribers)
bufferPublisher.connect().store(in: &subscribers)
RunLoop.main.run()
-
makeConnectable
makeConnectable
是一个操作符,它将一个冷发布者(lazy publisher)转换为一个可连接的发布者(connectable publisher)。冷发布者只有在有订阅者订阅时才会开始发布元素。而可连接的发布者即使没有任何订阅者,在被连接后(调用 connect
方法)也会开始发布元素。
connect
是可连接发布者的方法。调用connect
会启动数据流,使得发布者开始发布元素。这在需要多个订阅者共享同一个数据流的场景中非常有用。
- 共享数据流:如果你有一个发布者,多个订阅者需要共享同一个数据流,但你不希望每个订阅者都触发一次新的数据流(例如,多个视图组件需要显示相同的数据),你可以使用
makeConnectable
和connect
。 - 控制发布开始时间:有时你需要在特定时间点启动数据流,而不是在订阅时立即启动。这时
makeConnectable
和connect
就派上用场了。
假设我们有一个网络请求发布者,我们希望多个订阅者共享同一个网络请求的响应数据,而不是每个订阅者都触发一次新的网络请求。
import Combine
import Foundation
// 模拟网络请求的发布者
// 创建一个网络请求发布者,使用 URLSession.shared.dataTaskPublisher(for:) 来发起网络请求
// 并将响应数据解码为 Todo 对象。
let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
let publisher = URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: Todo.self, decoder: JSONDecoder())
// 将网络请求发布者转换为一个可连接的发布者。
.makeConnectable()
struct Todo: Decodable {
let id: Int
let title: String
}
// 创建一个可连接的发布者
let connectablePublisher = publisher.makeConnectable()
// 创建两个订阅者,它们将共享同一个网络请求的数据流。
// 第一个订阅者
let subscription1 = connectablePublisher.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Subscription 1: Finished")
case .failure(let error):
print("Subscription 1: Error: (error)")
}
}, receiveValue: { todo in
print("Subscription 1: Received Todo: (todo)")
})
// 第二个订阅者
let subscription2 = connectablePublisher.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Subscription 2: Finished")
case .failure(let error):
print("Subscription 2: Error: (error)")
}
}, receiveValue: { todo in
print("Subscription 2: Received Todo: (todo)")
})
// 调用 connect 方法启动数据流。此时,网络请求将被发起,数据流将开始发布。
let connection = connectablePublisher.connect()
// 取消连接和订阅
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
connection.cancel()
subscription1.cancel()
subscription2.cancel()
print("Cancelled subscriptions and connection")
}
Controlling Publishing with Connectable Publishers | Apple Developer Documentation
-
receive
receive
操作符用于指定发布者在哪个调度队列(线程)上发布值。它允许将事件从一个队列转移到另一个队列,例如从后台队列转移到主队列。
import Combine
let publisher = Just("Hello, Combine!")
.receive(on: DispatchQueue.main)
let subscription = publisher.sink { value in
print("Received on main thread: (value)")
}
-
send
在 Combine 中,send
操作符常见于 PassthroughSubject
和 CurrentValueSubject
,用于向这些 Subjects 发布新值。
import Combine
import Foundation
// 创建 PassthroughSubject
let passthroughSubject = PassthroughSubject<String, Never>()
// 订阅 PassthroughSubject
let passthroughSubscription = passthroughSubject
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("PassthroughSubject completed")
case .failure(let error):
print("PassthroughSubject error: (error)")
}
}, receiveValue: { value in
print("PassthroughSubject received value: (value)")
})
// 调用 send 方法向订阅者发送值和完成事件。
passthroughSubject.send("Hello")
passthroughSubject.send("Combine")
passthroughSubject.send(completion: .finished)
// 创建 CurrentValueSubject
let currentValueSubject = CurrentValueSubject<String, Never>("Initial value")
// 订阅 CurrentValueSubject
let currentValueSubscription = currentValueSubject
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("CurrentValueSubject completed")
case .failure(let error):
print("CurrentValueSubject error: (error)")
}
}, receiveValue: { value in
print("CurrentValueSubject received value: (value)")
})
// 调用 send 方法向订阅者发送值和完成事件。
currentValueSubject.send("Updated value")
currentValueSubject.send("Another value")
currentValueSubject.send(completion: .finished)
详细实例
为了展示这些操作符的实际用法,以下是一个详细的综合示例,演示如何将它们结合使用:
import Combine
import Foundation
// 模拟网络请求的发布者
let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
let publisher = URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: Todo.self, decoder: JSONDecoder())
.receive(on: DispatchQueue.main)
.makeConnectable()
struct Todo: Decodable {
let id: Int
let title: String
}
// ViewModel
class ViewModel: ObservableObject {
@Published var todoTitle: String = ""
}
let viewModel = ViewModel()
// 订阅并绑定数据到 ViewModel
let connection = publisher.connect()
let subscription1 = publisher
.map { $0.title }
.assign(to: .todoTitle, on: viewModel)
// 订阅并打印数据
let subscription2 = publisher.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Finished")
case .failure(let error):
print("Error: (error)")
}
}, receiveValue: { todo in
print("Received Todo: (todo)")
})
// 模拟取消连接
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
connection.cancel()
subscription1.cancel()
subscription2.cancel()
print("Cancelled subscriptions and connection")
}