从 Form/NavigationView 内的闭包引用 属性 并交换视图时 SwiftUI 内存泄漏
SwiftUI memory leak when referencing property from closure inside Form/NavigationView and swapping views
我有这样的东西:
struct SomeView: View {
@ObservedObject var viewModel: SomeViewModel
var body: some View {
NavigationView { // <- culprit
Button(action: { self.viewModel.logOut() }) { Text("X").frame(width: 40, height: 40) }
}
}
class SomeViewModel: ObservableObject {
func logOut() {
// changes global state, based on which the views are swapped, so `SomeView` is removed and replaced by a different one
}
}
按下按钮时,SomeView
关闭并呈现不同的视图。但是如果我检查内存图,SomeViewModel
仍然被分配,因为 self.viewModel.logOut()
在 Button 的动作闭包中被调用并且 Button 持有对 SomeViewModel
的引用。
有什么办法解决这个问题吗?
编辑:
实际上,如果不将按钮包裹在 NavigationView
中,则不会发生泄漏。我一包裹按钮,就会出现泄漏。在 VStack
中包装效果很好。但是包裹在 Form
中会再次产生泄漏。似乎这里有同样的问题:
我找到了一个解决办法:在你的动作中做一个弱 viewModel
。 Apple 似乎改变了闭包的行为。这意味着 NavigationView
正在存储对 viewModel 的强引用。经过几天的调试,终于对我有用了。
Button(action: {
[weak viewModel] in viewModel?.dismissButtonPressed.send(())
}) {
Image("crossmark")
.padding()
.foregroundColor(Color.white)
}
}
在你的问题中,将这样解决:
NavigationView {
[weak viewModel] in Button(action: { viewModel?.logOut() }) {
Text("X").frame(width: 40, height: 40)
}
}
在最新的 Xcode 11.5 和 iOS 13.5 上进行了测试。现在,关闭视图后,viewModel
已正确解除分配。
我有这样的东西:
struct SomeView: View {
@ObservedObject var viewModel: SomeViewModel
var body: some View {
NavigationView { // <- culprit
Button(action: { self.viewModel.logOut() }) { Text("X").frame(width: 40, height: 40) }
}
}
class SomeViewModel: ObservableObject {
func logOut() {
// changes global state, based on which the views are swapped, so `SomeView` is removed and replaced by a different one
}
}
按下按钮时,SomeView
关闭并呈现不同的视图。但是如果我检查内存图,SomeViewModel
仍然被分配,因为 self.viewModel.logOut()
在 Button 的动作闭包中被调用并且 Button 持有对 SomeViewModel
的引用。
有什么办法解决这个问题吗?
编辑:
实际上,如果不将按钮包裹在 NavigationView
中,则不会发生泄漏。我一包裹按钮,就会出现泄漏。在 VStack
中包装效果很好。但是包裹在 Form
中会再次产生泄漏。似乎这里有同样的问题:
我找到了一个解决办法:在你的动作中做一个弱 viewModel
。 Apple 似乎改变了闭包的行为。这意味着 NavigationView
正在存储对 viewModel 的强引用。经过几天的调试,终于对我有用了。
Button(action: {
[weak viewModel] in viewModel?.dismissButtonPressed.send(())
}) {
Image("crossmark")
.padding()
.foregroundColor(Color.white)
}
}
在你的问题中,将这样解决:
NavigationView {
[weak viewModel] in Button(action: { viewModel?.logOut() }) {
Text("X").frame(width: 40, height: 40)
}
}
在最新的 Xcode 11.5 和 iOS 13.5 上进行了测试。现在,关闭视图后,viewModel
已正确解除分配。