直接来看一个例子:
当点击+1按钮时,无论是@StateObject或是@ObservedObject其都表现出一致的状态,两个View都可以正常的显示当前按钮的点击次数,不过当点击刷新按钮时,CountViewState中的数值仍然正常,不过CountViewObserved中的计数值被清零了。
CountViewState中使用的是@StateObject保存变量,而CountViewObserved中使用@ObservedObject保存变量。
首先明确两个概念:(1)视图植树 (2)符合View协议的结构体,它们有各自的生命周期
SwiftUI 通过调用结构体实例的 body 获取对应的视图值。
@State和@StateObject的生命周期和视图植树的生命周期一致!而不是和符合View协议的结构体一致。我们必须牢记这一点。
一开始我的思路:由于CountViewState是结构体(结构体是值类型),每次点击父视图中的刷新按钮,CountViewState势必会重新初始化(CountViewState中的init方法每次都会输出),很多网络博客中都会说@StateObject的生命周期和View的生命周期一致,这个时候,你就会很疑惑。既然CountViewState:View 都重新初始化了,为什么@StateObject没有重新初始化。
那么这个例子中,CountViewState和CountViewObserved的视图植树有没有重新创建呢?答案是没有,原因在于视图的重新创建取决于source of truth有没有发生变化?
什么是source of truth(数据源),在SwiftUI中,@State @StateObject @ObserveObject @Environment @Environment Object都可以作为Source of truth。它们的变化,将导致依赖它们的视图需要重新计算并重绘。
很显然,CountViewState和CountViewObserved都没有依赖父视图Test1的Source of truth,所以,CountViewState和CountViewObserved的视图植树都没有被重新创建,既然视图植树没有被重新创建,那么就可以很好理解,@StateObject也不会被重新创建,所以CountViewState中的计数值没有被清空。
那么为什么CountViewObserved中的计数被清空了,因为@ObservedObject的生命周期在SwiftUI中很难预测和掌控。在这个例子中,CountViewObserved植树视图没有被重新创建,而@ObservedObject却重新初始化了,也就是说这个例子中@ObservedObject的生命周期比植树视图短。另外@ObservedObject的生命周期可能和植树视图生命周期一致或者大于,mp.weixin.qq.com/s/uHOaDNs48… 这个链接里的Test2 Test3验证了这一点。也就是说@ObservedObject的生命周期在SwiftUI中很难预测和掌控。
还有一点并不是body重新计算了(body里的let _ = print("body")),就意味着植树视图被重新创建了,以上例子可以看到CountViewObserved中的body被重新计算了,但它的植树视图并没有被重新创建,那么为什么CountViewObserved中的body被重新计算了(print输出了),而CountViewState中的body并没有重新计算?因为@ObservedObject发生了变化(init方法调用了)!
由于@ObservedObject的生命周期的难以预测和掌控,尽量使用@StateObject
参考链接:
mp.weixin.qq.com/s/uHOaDNs48…
www.fatbobman.com/posts/swift…
onevcat.com/2020/06/sta…