Combine @Published 属性 在未更新时也发送值
Combine @Published property send values also when not updated
我试图使用 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
}
}
}
我认为这是因为您为每次视图更新都创建了新的发布者,请尝试以下操作。 (使用 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()
}()
}
问题是 updatedImagesPublisher
是计算的 属性。这意味着您每次访问它时都会创建一个新实例。您的代码中发生了什么。 Text
对象订阅了updatedImagesPublisher
,当它接收到新值时,它会更新Model 的number
属性。 number
是@Published
属性,意思是每次改了都会调用objectWillChange
方法,重新创建body。 new Text
将订阅 new 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()
}()
我试图使用 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
}
}
}
我认为这是因为您为每次视图更新都创建了新的发布者,请尝试以下操作。 (使用 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()
}()
}
问题是 updatedImagesPublisher
是计算的 属性。这意味着您每次访问它时都会创建一个新实例。您的代码中发生了什么。 Text
对象订阅了updatedImagesPublisher
,当它接收到新值时,它会更新Model 的number
属性。 number
是@Published
属性,意思是每次改了都会调用objectWillChange
方法,重新创建body。 new Text
将订阅 new 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()
}()