swift - onReceive String.publisher 导致无限循环
问题描述
我在 View 中使用了两个发布者:
A:String.publisher
B: ObservableObject 包含一个@Published String 类型
如果我监视发布者 A,我会得到一个无限循环。但是监控发布者B是可以的!
import SwiftUI
import Combine
class Model: ObservableObject{
@Published var someBool = false
@Published var name:String = ""
}
struct ContentView: View {
// Publisher A
@State var name = ""
// Publisher B
@ObservedObject var model = Model()
var body: some View {
VStack {
// Plan A: lead to infinite loop!!!
TextField("Input Name", text: $name)
// Plan B: It's OK
//TextField("Input Name", text: $model.name)
.onReceive(name.publisher.reduce("", {t,c in
t + String(c)
})) {text in
print("change to \(text)")
self.model.someBool.toggle() //Plan A: infinite loop!!!
}
/*
.onReceive(model.$name){name in
print("change to \(name)")
self.model.someBool.toggle() //Plan B: It's OK!!!
}
*/
}
}
}
虽然我在onReceive()中修改了model.someBool的值,但是Plan B没问题,Plan A导致死循环。这是为什么???谢谢 :)
解决方案
希望,你需要一个真实的来源。如果您不喜欢使用您的模型,具有 State / Binding 对的等效代码可能看起来像
struct ContentView: View {
@State var name: String = ""
@State var flag = false
var body: some View {
let subject = CurrentValueSubject<String, Never>(name)
return VStack {
TextField("Input Name", text: $name).textFieldStyle(RoundedBorderTextFieldStyle()).padding()
.onReceive(subject) { name in
print("change to \(name)")
self.flag.toggle() // toggle every char typing
}
}
}
}
在您的示例中,我禁用(参见注释行)模型中的默认“请求”
import SwiftUI
import Combine
class Model: ObservableObject{
var someBool = false {
willSet {
print("will change to", newValue)
print("ask SwiftUI to update from model")
//self.objectWillChange.send()
}
didSet {
print(oldValue, "changed")
}
}
}
struct ContentView: View {
@State var name = ""
@StateObject var model = Model()
var body: some View {
VStack {
TextField("Input Name", text: $name).textFieldStyle(RoundedBorderTextFieldStyle())
.onReceive(name.publisher.reduce("", {t,c in
t + String(c)
})) {text in
print("change to \(text)")
self.model.someBool.toggle()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
打字时打印
true changed
change to Qw
will change to true
ask SwiftUI to update from model
false changed
change to Qwe
will change to false
ask SwiftUI to update from model
true changed
change to Qwer
will change to true
ask SwiftUI to update from model
false changed
change to Qwert
will change to false
ask SwiftUI to update from model
true changed
...
现在取消注释模型中的行
class Model: ObservableObject{
var someBool = false {
willSet {
print("will change to", newValue)
print("ask SwiftUI to update from model")
self.objectWillChange.send()
}
didSet {
print(oldValue, "changed")
}
}
}
并再次运行...它将无限循环打印
...
change to
will change to true
ask SwiftUI to update from model
false changed
change to
will change to false
ask SwiftUI to update from model
true changed
change to
will change to true
ask SwiftUI to update from model
false changed
change to
will change to false
ask SwiftUI to update from model
true changed
change to
...
你的模型发生了变化,SwiftUI 正在重新评估它的主体,因此模型再次变化......在一个循环中。
最小循环示例
import SwiftUI
import Combine
class Model: ObservableObject {
@Published var flag = false
}
struct ContentView: View {
@StateObject var model = Model()
var body: some View {
Color.yellow
.onReceive(model.$flag) {_ in
print(".")
self.model.flag.toggle()
}
}
}