[英]Swift Combine: Unexpected backpressure behaviour with zip operator
我有一個關於結合背壓組合中的zip
運算符的問題。
取以下代碼片段:
let sequencePublisher = Publishers.Sequence<Range<Int>, Never>(sequence: 0..<Int.max)
let subject = PassthroughSubject<String, Never>()
let handle = subject
.zip(sequencePublisher.print())
.print()
.sink { letters, digits in
print(letters, digits)
}
subject.send("a")
在操場上執行此操作時,輸出如下:
receive subscription: (0..<9223372036854775807)
receive subscription: (Zip)
request unlimited
request unlimited
receive value: (0)
receive value: (1)
receive value: (2)
receive value: (3)
receive value: (4)
receive value: (5)
receive value: (6)
receive value: (7)
...
在 iOS 設備上執行時,由於內存問題,代碼在幾秒鍾后崩潰。
根本原因可以在上面的第四行中看到,其中zip
從sequencePublisher
請求無限數量的值。 由於sequencePublisher
提供了整個范圍的Int
值,這會導致內存溢出。
我想知道的:
zip
等待每個發布者的一個值,然后再組合它們並推動它們我的期望是zip
只向每個發布者請求一個值,等待它們到達,並且只在從每個發布者收到一個值時才請求下一個值。
在這種特殊情況下,我嘗試構建一種行為,其中將序列號分配給subject
產生的每個值。 但是,我可以想象,當zip
結合來自發布頻率非常不同的發布者的值時,這總是一個問題。
在zip
運算符中使用背壓似乎是解決該問題的完美工具。 你知道為什么不是這樣嗎? 這是一個錯誤還是故意的? 如果是故意的,為什么?
謝謝你們
看來Sequence發布者只是不現實。 它似乎對背壓沒有反應; 它只是一次噴出整個序列,這在一個發布應該是異步的世界中是沒有意義的。 如果您將Int.max
更改為 3,則沒有問題。 :) 我不知道這是 Sequence 發布者的整個概念中的錯誤還是缺陷。
但是,對於您的實際用例來說真的沒有問題,因為有一種更好的方法可以為 Subject 的每個發射分配一個連續的數字,即scan
。
這是一個更現實的方法:
func delay(_ delay:Double, closure:@escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
class ViewController : UIViewController {
var storage = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
let subject = PassthroughSubject<String, Never>()
subject.scan(("",0)) {t,s in (s,t.1+1)}
.sink { print($0.0, $0.1)
}.store(in:&storage)
delay(1) {
subject.send("a") // a 1
delay(1) {
subject.send("b") // b 2
}
}
}
}
這假設您有其他一些原因需要每個連續的枚舉通過管道傳遞。 但是,如果您的唯一目標是在每個信號到達接收sink
本身時對其進行枚舉,那么您可以讓接收sink
本身維護一個計數器(這很容易做到,因為它是一個閉包):
var storage = Set<AnyCancellable>()
let subject = PassthroughSubject<String, Never>()
override func viewDidLoad() {
super.viewDidLoad()
var counter = 1
subject
.sink {print($0, counter); counter += 1}
.store(in:&storage)
delay(1) {
self.subject.send("a") // a 1
self.subject.send("b") // b 2
}
}
組合的 zip 運算符:
除了基於掃描的解決方案之外,您還可以通過控制下游的背壓需求或使用自定義 zip 運算符來避免該問題。
我設法開發了一個自定義 zip 運算符,它在兩個方面與原始 zip 運算符不同:
此處包含的代碼有點廣泛,但請隨時在此 repo https://github.com/SergeBouts/XCombine 中查看
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.