【发布时间】:2020-01-31 20:26:56
【问题描述】:
在命令式 Swift 中,通常使用计算属性来方便地访问数据而无需复制状态。
假设我有这个类用于命令式 MVC:
class ImperativeUserManager {
private(set) var currentUser: User? {
didSet {
if oldValue != currentUser {
NotificationCenter.default.post(name: NSNotification.Name("userStateDidChange"), object: nil)
// Observers that receive this notification might then check either currentUser or userIsLoggedIn for the latest state
}
}
}
var userIsLoggedIn: Bool {
currentUser != nil
}
// ...
}
如果我想用 Combine 创建一个反应式等价物,例如为了与 SwiftUI 一起使用,我可以轻松地将 @Published 添加到存储属性以生成 Publishers,但不适用于计算属性。
@Published var userIsLoggedIn: Bool { // Error: Property wrapper cannot be applied to a computed property
currentUser != nil
}
我可以想到各种解决方法。我可以改为存储我的计算属性并保持更新。
选项 1:使用属性观察器:
class ReactiveUserManager1: ObservableObject {
@Published private(set) var currentUser: User? {
didSet {
userIsLoggedIn = currentUser != nil
}
}
@Published private(set) var userIsLoggedIn: Bool = false
// ...
}
选项 2:在我自己的班级中使用 Subscriber:
class ReactiveUserManager2: ObservableObject {
@Published private(set) var currentUser: User?
@Published private(set) var userIsLoggedIn: Bool = false
private var subscribers = Set<AnyCancellable>()
init() {
$currentUser
.map { $0 != nil }
.assign(to: \.userIsLoggedIn, on: self)
.store(in: &subscribers)
}
// ...
}
但是,这些变通方法不如计算属性优雅。它们复制状态并且它们不会同时更新两个属性。
将Publisher 添加到Combine 中的计算属性的适当等效方法是什么?
【问题讨论】:
-
计算属性是一种派生属性。它们的值取决于依赖项的值。仅出于这个原因,可以说他们永远不会表现得像
ObservableObject。您天生就假设ObservableObject对象应该能够具有变异能力,根据定义,Computed Property 并非如此。 -
您找到解决方案了吗?我处于完全相同的情况,我想避免状态并且仍然能够发布
-
感谢
private(set)解决方案。对我帮助很大。 -
让所有
subscribers保持在一个好主意!我会采纳的
标签: ios swift swiftui reactive combine