当 Publisher 参数没有要发出的任何新值时,为啥会调用 SwiftUI View 上的 onReceive 块?

Posted

技术标签:

【中文标题】当 Publisher 参数没有要发出的任何新值时,为啥会调用 SwiftUI View 上的 onReceive 块?【英文标题】:Why is onReceive block on SwiftUI View called when Publisher argument doesn't have any new values to emit?当 Publisher 参数没有要发出的任何新值时,为什么会调用 SwiftUI View 上的 onReceive 块? 【发布时间】:2020-05-15 02:12:13 【问题描述】:

我已将 onReceive 修饰符附加到 SwiftUI 中的视图。订阅的目的是响应 @Published 属性 greeting 的更改,这是视图模型对象的一部分。

视图包含分段选取器。分段选取器使用视图模型中的另一个属性——index

莫名其妙地,当用户更改分段选择器选择时,即使发布者greeting 没有更改,也会调用 onReceive 块。


import SwiftUI
import Combine


class Model: ObservableObject 
    @Published var selectedIndex = 0
    @Published var greeting = "initial"



struct ContentView: View 
    @ObservedObject var model: Model = Model()
    let pickerTitles = ["One", "Two", "Three"]

    var body: some View 
        VStack 
            return Picker("Options", selection: $model.selectedIndex) 
                ForEach(0 ..< pickerTitles.count)  index in
                    Text(self.pickerTitles[index])
                

            .pickerStyle(SegmentedPickerStyle())
        
        .onReceive([model.greeting].publisher)str in
            print(str)
        

    

发生了什么事?上面的代码是整个代码库。

回复:总结 我在 .onReceive 块中指定了一个发布者。那个指定的属性——greeting——永远不会改变。当另一个属性——index——被同一 ObservedObject 中的 Picker 更改时,.onReceive 块被神秘地调用。

【问题讨论】:

【参考方案1】:

您在代码中使用Publishers.Sequence,它将按顺序逐一发布元素。

使用您的代码,[model.greeting].publisher 会重新评估,并且每次评估 body 时都会重新创建发布者,这意味着发布的元素会重置为第一个。

通过下面的代码,你可以发现x被打印出来,每次body被评估:

struct ContentView: View 
    @ObservedObject var model: Model = Model()
    let pickerTitles = ["One", "Two", "Three"]

    var body: some View 
        VStack 
            return Picker("Options", selection: $model.selectedIndex) 
                ForEach(0 ..< pickerTitles.count)  index in
                    Text(self.pickerTitles[index])
                

            .pickerStyle(SegmentedPickerStyle())
        
        .onReceive(["x"].publisher)str in
            print(str)
        
    


如果您只想在greeting 上接收更改,您可以这样写:

struct ContentView: View 
    @ObservedObject var model: Model = Model()
    let pickerTitles = ["One", "Two", "Three"]

    var body: some View 
        VStack 
            return Picker("Options", selection: $model.selectedIndex) 
                ForEach(0 ..< pickerTitles.count)  index in
                    Text(self.pickerTitles[index])
                

            .pickerStyle(SegmentedPickerStyle())
        
        .onReceive(model.$greeting)str in
            print(str)
        
    

【讨论】:

我必须考虑这一点,但我想我理解你的观点,即每次用户在 Picker 中选择不同的段时,Combine 库都会观察视图模型的变化,然后核弹原始视图。重新初始化了一个新的视图结构。由于 .publisher 似乎默认为 CurrentSubject 发布者,因此在创建新 View 结构时会发出初始值(“初始值”)。谢谢你。我会到处玩。顺便说一句,转移到价值对象的世界是很棘手的。在 UIKit 中,几乎所有东西都是引用对象,并且不会突然“消失”。 @SmallTalk,我不确定你是否理解正确,但你的.publisher 放在一个数组上,所以它与CurrentValueSubject 无关。您的.publisher 调用publisher property on Sequence。您的代码 [model.greeting].publisher 会忽略模型的所有已发布或可观察的特征,并从 ["initial"] 创建一个 Publishers.Sequence 清除我有更多的理解差距。感谢您的评论。我现在要澄清的唯一一点是:如果 State 或 ObservedObject 中的 Published 属性发生变化,View 结构是否会被删除并从头开始重新初始化。似乎答案是肯定的。 (我知道 SwiftUI 会保留当前的 ​​State 值,并且可能在丢弃 View 和使用保留值从头开始重新创建之间“重新连接”对 ObservedObject 的引用。这是 SwiftUI 中一个奇怪的新世界。 @SmallTalk,看来你说对了。 SwiftUI 在它认为需要的任何时候评估body。返回的视图有点像蓝图,SwiftUI 将其与旧的body 进行比较,并更新已更改的实际可视组件。像我这样的老程序员可能需要大量的时间来适应它...... 感谢您的帮助。我现在可以更好地理解了。

以上是关于当 Publisher 参数没有要发出的任何新值时,为啥会调用 SwiftUI View 上的 onReceive 块?的主要内容,如果未能解决你的问题,请参考以下文章

angular管道

存在新值时添加新的 Google 工作表行

当一个函数无返回值时,函数的类型应定义为啥

Ngrx - 选择器在存储更改后没有发出新值

UITableViewCell detailTextLabel在为其分配新值时不会更新其文本

wpf点击checkbox触发了两次