onReceive String.publisher 导致死循环

onReceive String.publisher lead to infinite loop

我在视图中使用了两个发布者:

答: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值,但是方案B没问题,方案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()
            }
    }
}