onReceive String.publisher приведет к бесконечному циклу - PullRequest
0 голосов
/ 10 марта 2020

Я использую двух издателей в представлении:

A: String.publisher

B: ObservableObject включает один тип @Published String

Если я наблюдаю за издателем A, я получаю бесконечный l oop. Но с издателем мониторинга 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!!!
                }
                */
            }
        }
    }

Хотя я изменил значение model.someBool в onReceive (), но с планом B все в порядке, план A ведет к бесконечному l oop. Это почему??? Спасибо:)

1 Ответ

0 голосов
/ 10 марта 2020

Надеюсь, вам нужен один источник истины. Если вы не любите использовать вашу модель, эквивалентный код с парой 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 = ""
    @ObservedObject 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")
        }
    }
}

и запустите ее снова ... она будет печататься бесконечно долго. oop

...
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 переоценивает свое тело, и благодаря этому модель снова меняется ... в al oop.

Минимальное l oop пример

import SwiftUI
import Combine

class Model: ObservableObject {
    @Published var flag = false
}
struct ContentView: View {
    @ObservedObject var model = Model()
    var body: some View {
            Color.yellow
                .onReceive(model.$flag) {_ in
                    print(".")
                self.model.flag.toggle()
            }
    }
}
...