2

我在 viewModels 中使用 Combine 来更新视图。但是,如果我将 AnyCancellable 对象存储到一组 AnyCancellable 中,则永远不会调用 deinit 方法。我使用 deinit 取消所有可取消对象。

struct View1: View {

    @ObservedObject var viewModel:ViewTextModel = ViewTextModel()
    @Injected var appActions:AppActions

    var body: some View {
        VStack {
            Text(self.viewModel.viewText)

            Button(action: {
                self.appActions.goToView2()
            }) {
                Text("Go to view \(self.viewModel.viewText)")
            }
        }
    }
}
class ViewTextModel: ObservableObject {
    @Published var viewText: String

    private var cancellables = Set<AnyCancellable>()

    init(state:AppState) {
        // initial state
        viewText = "view  \(state.view)"
        // updated state
        state.$view.removeDuplicates().map{ "view \($0)"}.assign(to: \.viewText, on: self).store(in: &cancellables)
    }

    deinit {
        cancellables.forEach { $0.cancel() }
    } 
}

每次重建视图时,都会实例化一个新的视图模型,但不会破坏旧的视图模型。viewText属性在每个实例上更新state.$view.removeDuplicates().map{ "view \($0)"}.assign(to: \.viewText, on: self).store(in: &cancellables)

如果我不将可取消对象存储在集合中,deinit则调用但viewText如果当前视图的状态发生更改则不会更新。

您是否知道如何在不增加视图模型实例的情况下管理状态更新?

谢谢

4

1 回答 1

1

您可以使用sink而不是assign

state.$view
    .removeDuplicates()
    .sink { [weak self] in self?.viewText = $0 }
    .store(in: &cancellables)

但我完全怀疑是否需要使用 Combine。只需使用计算属性:

class ViewTextModel: ObservableObject {
    @Published var state: AppState

    var viewText: String { "view \(state.view)" }
}

更新

如果您的部署目标是 iOS 14(或 macOS 11)或更高版本:

因为您要存储到@Published,所以可以改用assign(to:)运算符。它为您管理订阅而不返回AnyCancellable.

state.$view
    .removeDuplicates()
    .map { "view \($0)" }
    .assign(to: &$viewText)
    // returns Void, so nothing to store
于 2020-03-17T21:39:41.807 回答