ObervableObject 被多次初始化,并且没有刷新我的视图
ObervableObject being init multiple time, and not refreshing my view
我有这样的结构
contentView {
navigationView{
foreach {
NavigationLink(ViewA(id: id))
}
}
}
///其中View A在View Appear中包含一个请求触发器
struct ViewA: View {
@State var filterString: String = ""
var id: String!
@ObservedObject var model: ListObj = ListObj()
init(id: String) {
self.id = id
}
var body: some View {
VStack {
SearchBarView(searchText: $filterString)
List {
ForEach(model.items.filter({ filterString.isEmpty || [=11=].id.contains(filterString) || [=11=].name.contains(filterString) }), id: \.id) { item in
NavigationLink(destination: ViewB(id: item.id)) {
VStack {
Text("\(item.name) ")
}
}
}
}
}
.onAppear {
self.model.getListObj(id: self.id) //api request, fill data and call objectWillChange.send()
}
}
}
}
ViewB 的代码与 ViewB 相同,
接收id,存储,请求api收集数据。
但是 viewB 列表没有被刷新。
我还注意到 viewB 的
@ObservedObject var model: model = model()
被多次实例化
正在调试,
我发现每个 navigationLink 实例甚至在它被触发之前都是目的地。
这通常不是问题,
但在我的情况下,我觉得 ViewB 模型被实例化了 2 次,而我的 onApear 调用了错误的模型,这是 self.objectWillChange.send() 没有刷新我的视图的原因
这里有两个问题:
- SwiftUI 使用值类型,每次通过
body
. 都会一遍又一遍地初始化
- 关于#1,
NavigationLink
不懒惰。
#1
每次调用 ViewA.init(...)
时都会实例化一个新的 ListObj
。 ObservedObject
与 @State
的工作方式不同 SwiftUI 在整个屏幕生命周期中都会为您仔细跟踪它。 SwiftUI 假定 @ObservedObject
的最终所有权存在于它所使用的 View
之上的某个级别。
换句话说,你应该几乎总是避免像 @ObservedObject var myObject = MyObservableObject()
这样的事情。
(请注意,即使您执行了 @State var model = ListObj()
,它也会每次都被实例化。但是因为它是 @State
,SwiftUI 会在调用 body
之前用原始实例替换新实例。 )
#2
除此之外,NavigationLink
也不懒惰。每次实例化 NavigationLink
时,都会传递一个新实例化的 ViewA
,它会实例化您的 ListObj
。
所以对于初学者来说,您可以做的一件事是制作一个 LazyView
来延迟实例化,直到 NavigationLink.destination.body
实际被调用:
// Use this to delay instantiation when using `NavigationLink`, etc...
struct LazyView<Content: View>: View {
var content: () -> Content
var body: some View {
self.content()
}
}
现在您可以执行 NavigationLink(destination: LazyView { ViewA() })
并且 ViewA
的实例化将推迟到 destination
实际显示。
只需使用 LazyView
即可解决您当前的问题 只要它是层次结构中的顶层视图,就像您将它推入 NavigationView
或者如果你出示它。
然而,这是@user3441734 的评论出现的地方。你真正需要做的是在你的 View
之外的某个地方保留 model
的所有权,因为在 #1 中有解释。
如果您的 @ObservedObject
被多次初始化,那是因为对象的所有者在每次状态更改时都会被刷新和重新创建。如果您的应用是 iOS 14 及更高版本,请尝试使用 @StateObject
。它可以防止在刷新视图时重新创建对象。
https://developer.apple.com/documentation/swiftui/stateobject
When a view creates its own @ObservedObject instance it is recreated
every time a view is discarded and redrawn. On the contrary a @State
variable will keep its value when a view is redrawn. A @StateObject is
a combination of @ObservedObject and @State - the instance of the
ViewModel will be kept and reused even after a view is discarded and
redrawn
我有这样的结构
contentView {
navigationView{
foreach {
NavigationLink(ViewA(id: id))
}
}
}
///其中View A在View Appear中包含一个请求触发器
struct ViewA: View {
@State var filterString: String = ""
var id: String!
@ObservedObject var model: ListObj = ListObj()
init(id: String) {
self.id = id
}
var body: some View {
VStack {
SearchBarView(searchText: $filterString)
List {
ForEach(model.items.filter({ filterString.isEmpty || [=11=].id.contains(filterString) || [=11=].name.contains(filterString) }), id: \.id) { item in
NavigationLink(destination: ViewB(id: item.id)) {
VStack {
Text("\(item.name) ")
}
}
}
}
}
.onAppear {
self.model.getListObj(id: self.id) //api request, fill data and call objectWillChange.send()
}
}
}
}
ViewB 的代码与 ViewB 相同, 接收id,存储,请求api收集数据。
但是 viewB 列表没有被刷新。 我还注意到 viewB 的
@ObservedObject var model: model = model()
被多次实例化
正在调试, 我发现每个 navigationLink 实例甚至在它被触发之前都是目的地。 这通常不是问题,
但在我的情况下,我觉得 ViewB 模型被实例化了 2 次,而我的 onApear 调用了错误的模型,这是 self.objectWillChange.send() 没有刷新我的视图的原因
这里有两个问题:
- SwiftUI 使用值类型,每次通过
body
. 都会一遍又一遍地初始化
- 关于#1,
NavigationLink
不懒惰。
#1
每次调用 ViewA.init(...)
时都会实例化一个新的 ListObj
。 ObservedObject
与 @State
的工作方式不同 SwiftUI 在整个屏幕生命周期中都会为您仔细跟踪它。 SwiftUI 假定 @ObservedObject
的最终所有权存在于它所使用的 View
之上的某个级别。
换句话说,你应该几乎总是避免像 @ObservedObject var myObject = MyObservableObject()
这样的事情。
(请注意,即使您执行了 @State var model = ListObj()
,它也会每次都被实例化。但是因为它是 @State
,SwiftUI 会在调用 body
之前用原始实例替换新实例。 )
#2
除此之外,NavigationLink
也不懒惰。每次实例化 NavigationLink
时,都会传递一个新实例化的 ViewA
,它会实例化您的 ListObj
。
所以对于初学者来说,您可以做的一件事是制作一个 LazyView
来延迟实例化,直到 NavigationLink.destination.body
实际被调用:
// Use this to delay instantiation when using `NavigationLink`, etc...
struct LazyView<Content: View>: View {
var content: () -> Content
var body: some View {
self.content()
}
}
现在您可以执行 NavigationLink(destination: LazyView { ViewA() })
并且 ViewA
的实例化将推迟到 destination
实际显示。
只需使用 LazyView
即可解决您当前的问题 只要它是层次结构中的顶层视图,就像您将它推入 NavigationView
或者如果你出示它。
然而,这是@user3441734 的评论出现的地方。你真正需要做的是在你的 View
之外的某个地方保留 model
的所有权,因为在 #1 中有解释。
如果您的 @ObservedObject
被多次初始化,那是因为对象的所有者在每次状态更改时都会被刷新和重新创建。如果您的应用是 iOS 14 及更高版本,请尝试使用 @StateObject
。它可以防止在刷新视图时重新创建对象。
https://developer.apple.com/documentation/swiftui/stateobject
When a view creates its own @ObservedObject instance it is recreated every time a view is discarded and redrawn. On the contrary a @State variable will keep its value when a view is redrawn. A @StateObject is a combination of @ObservedObject and @State - the instance of the ViewModel will be kept and reused even after a view is discarded and redrawn