【问题标题】:Combine @Published property send values also when not updated在未更新时也组合 @Published 属性发送值
【发布时间】:2020-08-21 18:18:21
【问题描述】:

我试图使用 SwiftUI 和 Combine 创建一个动态表单,它根据另一个输入(在示例中为 myString)加载输入选项(在示例中为 number)。

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

我认为预期的行为是 $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
            }
    }
}

【问题讨论】:

    标签: swift combine


    【解决方案1】:

    问题是updatedImagesPublisher 是一个计算属性。这意味着您每次访问它时都会创建一个新实例。你的代码会发生什么。 Text 对象订阅updatedImagesPublisher,当它接收到一个新值时,它会更新模型的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()
    }()
    

    【讨论】:

    【解决方案2】:

    我认为这是因为您为每个视图更新创建了新的发布者,请尝试以下操作。 (使用 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()
        }()
    }
    

    【讨论】:

      猜你喜欢
      • 2022-01-04
      • 2020-11-12
      • 1970-01-01
      • 2021-12-27
      • 1970-01-01
      • 2020-04-30
      • 2020-10-21
      • 1970-01-01
      • 2021-04-01
      相关资源
      最近更新 更多