【问题标题】:SwiftUI View (apparently) laid out before init runsSwiftUI 视图(显然)在 init 运行之前布局
【发布时间】:2020-05-07 15:25:31
【问题描述】:

TL;DR

似乎下面的ContentViewinit 运行之前评估了主体的if 语句。是否存在竞争条件,或者我的心智模型有问题?

荣誉

向 Asperi 致敬,他提供了解决当今问题的 state-initializer equivalent

代码

为什么ContentView 显示“dummy is nil”?似乎有些东西在之前初始化器设置dummy 被关闭了。解决问题的第二个任务是什么?

class Dummy {  }

struct ContentView: View {
    @State private var dummy : Dummy?

    init() {
        print("Init") // In either case, is printed before "Body"

        // Using this assignment, "dummy is nil" shows on screen.
        self.dummy = Dummy()

        // Using this, "dummy is non-nil" shows on screen.
        // From https://stackoverflow.com/questions/61650040/swiftui-initializer-apparent-circularity
        // self._dummy = State(initialValue: Dummy())
    }

    var body: some View {
        print("Body")
        return ZStack {
            if dummy == nil {              // Decision seems to be taken
                Text("dummy is nil"    )   // before init() has finished.
            } else { 
                Text("dummy is non-nil") 
            }
        }
    }
}

【问题讨论】:

  • 这只是@State 属性包装器的本质...接受它:)
  • 我没有太多选择......但仍然很奇怪。这出现在一个商业应用程序中,而不仅仅是一个玩具示例。我尝试了[长长的事情清单],就在今天早上(在其他地方)看到了你的答案,并认为“这不应该有所作为......但我会试试看。”
  • 不是before init() has finished。只是self.dummy = Dummy() 什么都不做,跳过了。如果您需要初始化状态,那么您必须直接在属性声明中或通过self._dummy 在初始化中进行,或者在视图已经存在时进行更改,例如在.onAppear 及更高版本中。
  • 我不太明白你所说的“什么都不做”是什么意思。编译器是否有权优化状态分配?在我的例子中,假人是PlayerViewModel 的一个实例,你在两个月前对它进行了很有帮助的解释。我确实尝试将其设置为.onAppear,但那时它已经不是零了。它只是没有出现在屏幕上,因为声明式布局中的 if 子句已将其排除在外。
  • 我在@State vars 中看到了在初始化程序中设置的图像的相同问题(具有相同的修复)。 IE。在View 首次出现时,没有图像。在我看来,它真的像依赖管理中的竞争条件。

标签: initialization swiftui race-condition property-wrapper


【解决方案1】:

共识是它是一个看起来像错误的功能。

Helpful discussion 在 Swift 论坛中。 Also here。重点(为清晰起见进行了编辑)包括:

首先不这样做的充分理由:

  • 你不应该在View init 期间发生变异,因为那会在 body父视图的调用

  • @State SwiftUI 中的变量不应从您通过初始化程序传递的数据初始化;由于模型是在视图之外维护的,因此无法保证该值会真正被使用。

  • 不要尝试在init 期间覆盖@State 的初始值。它只会在第一次视图创建期间工作一次(重要:不是值初始化),并且只有当视图树中的该视图被重新创建/替换为相同类型但内部不同 ID 的不同视图时。

“这是一个错误”的位置(接近我的):

  • 这是由于编译器对属性包装器的实现存在人为限制。解决方法是直接通过_value初始化后备存储

解释方式和原因:

  • @State 中的值将始终使用您在init 中传递的值进行初始化,这很简单 Swift。但是,在下一次 body 调用之前,SwiftUI 将调用 update 方法并重新向其中注入一个值,如果之前的值将覆盖您从 init 中的值。

  • 这不是错误! :man_facepalming:它按预期/广告工作。 ...@State 属性包装器向视图添加了一种可能的变异方式,这也意味着视图私有,而不是从父级初始化,等等。

这是飞行员的错误(可能是真的):

  • 除了人们误解了 State 的用途之外,这里没有问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-23
    相关资源
    最近更新 更多