【发布时间】:2020-06-19 16:41:54
【问题描述】:
我在 SwiftUI 和 Combine 中的内存管理方面遇到了一些问题。
例如,如果我有一个 NavigationView,然后导航到一个带有 TextField 的详细视图,然后在 TextField 中输入一个值并点击后退按钮,那么下次我转到该视图时,TextField 具有之前输入的值.
我注意到在关闭详细视图后视图模型仍在内存中,这可能就是 TextField 仍然保持值的原因。
在 UIKit 中,当关闭 ViewController 时,视图模型将被释放,然后在 ViewController 出现时再次创建。这似乎不是这里的情况。
我为这个问题附上了一些最低限度的可复制代码。
import SwiftUI
import Combine
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: OtherView()) {
Text("Press Here")
}
}
}
}
struct OtherView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
VStack {
TextField("Something", text: $viewModel.enteredText)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
print("Tap")
}) {
Text("Tapping")
}.disabled(!viewModel.isValid)
}
}
}
class ViewModel: ObservableObject {
@Published var enteredText = ""
var isValid = false
var cancellable: AnyCancellable?
init() {
cancellable = textValidatedPublisher.receive(on: RunLoop.main)
.assign(to: \.isValid, on: self)
}
deinit {
cancellable?.cancel()
}
var textValidatedPublisher: AnyPublisher<Bool, Never> {
$enteredText.map {
$0.count > 1
}.eraseToAnyPublisher()
}
}
我还注意到,例如,如果我在 OtherView 之后添加另一个视图,比如说 SomeOtherView,那么每次我从 OtherView 输入 TextField 时,就会调用 SomeOtherView 的视图模型中的 deinit。谁能解释一下为什么会这样?
【问题讨论】:
-
发生这种情况是因为当您返回时
ContentView没有更改,因此它的body永远不会重新评估。这意味着OtherView实例及其视图模型仅创建一次 - 第一次评估ContentView的主体。 -
不是错误。这个
NavigationLink(destination: OtherView())在body调用上创建OtherView(),但不像人们想象的那样在导航时创建。因此,一旦您在OtherView构造时间创建视图模型,它的一个实例将始终以这种方法存在。 ...和上面的@NewDev +。 -
@Asperi 那么有没有办法在导航时实例化 OtherView?
-
此外,我注意到如果我更改 ContetView 并重新评估视图,那么我将在内存中有两个 ViewModel