合并@Published属性也会在未更新时发送值

时间:2020-05-06 17:09:37

标签: swift combine

我试图使用SwiftUI和Combine创建一个动态窗体,该窗体基于另一个输入(在示例number中)加载一个输入(在示例中,myString)的选项。 / p>

问题在于即使值从未更改,Combo堆栈也将连续执行,发出大量网络请求(在此示例中,通过延迟进行模拟)。

我认为预期的行为是$myString仅在其更改时才发布值。

class MyModel: ObservableObject {

    // My first choice on the form
    @Published var myString: String = "Jhon"

    // My choice that depends on myString
    @Published var number: Int?

    var updatedImagesPublisher: AnyPublisher<Int, Never> {
        return $myString
            .removeDuplicates()
            .print()
            .flatMap { newImageType in
                return Future<Int, Never> { promise in

                    print("Executing...")

                    // Simulate network request
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                        let newNumber = Int.random(in: 1...200)
                        return promise(.success(newNumber))
                    }
                }
        }
        .receive(on: DispatchQueue.main)
        .eraseToAnyPublisher()
    }
}

struct ContentView: View {

    @ObservedObject var model: MyModel = MyModel()

    var body: some View {
        Text("\(model.number ?? -100)")
            .onReceive(model.updatedImagesPublisher) { newNumber in
                self.model.number = newNumber
            }
    }
}

2 个答案:

答案 0 :(得分:3)

问题是updatedImagesPublisher是一个计算属性。这意味着您每次访问都会创建一个新实例。代码中会发生什么。 Text对象订阅updatedImagesPublisher,当它收到一个新值时,它将更新Model的number属性。 number@Published的属性,这意味着objectWillChange方法将在您每次更改它时被调用,并且将重新创建主体。新的Text将订阅新的updatedImagesPublisher(因为它是计算属性)并再次接收该值。为了避免这种行为,只需使用惰性属性而不是计算属性。

lazy var updatedImagesPublisher: AnyPublisher<Int, Never> = {
    return $myString
        .removeDuplicates()
        .print()
        .flatMap { newImageType in
            return Future<Int, Never> { promise in

                print("Executing...")

                // Simulate network request
                DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                    let newNumber = Int.random(in: 1...200)
                    return promise(.success(newNumber))
                }
            }
    }
    .receive(on: DispatchQueue.main)
    .eraseToAnyPublisher()
}()

答案 1 :(得分:1)

我认为这是因为您为每个视图更新创建了新的发布者,请尝试以下操作。 (已通过Xcode 11.4测试)

class MyModel: ObservableObject {

    // My first choice on the form
    @Published var myString: String = "Jhon"

    // My choice that depends on myString
    @Published var number: Int?

    lazy var updatedImagesPublisher: AnyPublisher<Int, Never> = {
        return $myString
            .removeDuplicates()
            .print()
            .flatMap { newImageType in
                return Future<Int, Never> { promise in

                    print("Executing...")

                    // Simulate network request
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                        let newNumber = Int.random(in: 1...200)
                        return promise(.success(newNumber))
                    }
                }
        }
        .receive(on: DispatchQueue.main)
        .eraseToAnyPublisher()
    }()
}