在初始化运行之前布局的SwiftUI视图(显然)

时间:2020-05-07 15:25:31

标签: initialization swiftui race-condition property-wrapper

TL; DR

似乎ContentView会在if运行之前评估主体的init语句。是否存在比赛条件,或者我的心理模型是否混乱?

荣誉

向阿斯佩里大喊大叫,阿斯佩里提供了state-initializer equivalent来解决当今的问题。

代码

为什么ContentView显示“虚拟为零”?似乎在初始化器设置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") 
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

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

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

首先没有这样做的充分理由:

  • 您不应该在View初始化期间进行突变,因为它会在 body调用父视图

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

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

    < / li>

“这是功能失常的”位置(接近我的位置):

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

说明方式和原因:

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

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

这是试点错误(可能是真的):

  • 这里没有问题,只是人们误解了国家的目的。