从 Form/NavigationView 中的闭包引用属性并交换视图时,SwiftUI 内存泄漏

Nev*_*oon 8 memory-leaks ios swiftui

我有这样的事情:

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
  }
}
Run Code Online (Sandbox Code Playgroud)

当按下按钮时,SomeView关闭并呈现不同的视图。但是如果我检查内存图,SomeViewModel仍然是分配的,因为self.viewModel.logOut()在 Button 的动作闭包中被调用并且 Button 持有对 的引用SomeViewModel

有没有办法解决这个问题?

编辑:实际上,当不将按钮包裹在 中时NavigationView,没有泄漏。我一包住按钮,就会出现泄漏。包裹起来效果VStack很好。但是包裹起来又会Form产生泄漏。这里似乎是同样的问题:SwiftUI - 可能的内存泄漏

小智 9

我找到了一个解决方案:viewModel在你的行动中做一个弱者。苹果似乎改变了闭包的行为。这意味着NavigationView正在存储对 viewModel 的强引用。经过几天的调试,它终于对我有用了。

Button(action: { 
    [weak viewModel] in viewModel?.dismissButtonPressed.send(())
}) {
    Image("crossmark")
        .padding()
        .foregroundColor(Color.white)
    }
}
Run Code Online (Sandbox Code Playgroud)

在你的问题中,这将是这样解决的:

NavigationView { 
    [weak viewModel] in Button(action: { viewModel?.logOut() }) {
        Text("X").frame(width: 40, height: 40)
    }
}
Run Code Online (Sandbox Code Playgroud)

在最新的 Xcode 11.5 和 iOS 13.5 上测试。现在,在关闭视图后,它viewModel被正确地解除分配。

  • 自从我一直在努力解决这个问题以来,我已经转向全局状态+修改该状态的操作,而不是使用本地视图模型。实际上,我什至也停止使用 NavigationView,因为它还有其他一些问题,转而采用自定义路由处理。但这看起来确实合法,所以我现在将其标记为正确答案。谢谢! (2认同)