Swiftui ObservedObject如何订阅其@Published成员
我正试图像属性包装器一样编写自己的Swiftui ObservedObject如何订阅其@Published成员,swiftui,observers,combine,Swiftui,Observers,Combine,我正试图像属性包装器一样编写自己的@Published——我有数百个成员值,我只希望在它们实际按相等方式更改时发出发布值(而不是每次设置它们,这是默认值) 属性包装器本身很简单 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) @propertyWrapper public struct EqPublished<Value: Equatable> { public var wrappedValue: Val
@Published
——我有数百个成员值,我只希望在它们实际按相等方式更改时发出发布值(而不是每次设置它们,这是默认值)
属性包装器本身很简单
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@propertyWrapper public struct EqPublished<Value: Equatable> {
public var wrappedValue: Value
{
willSet {
_projectedValue.send(newValue)
}
}
public var projectedValue: AnyPublisher<Value, Never>
private var _projectedValue: CurrentValueSubject<Value, Never>
public init(wrappedValue: Value)
{
self.wrappedValue = wrappedValue
self._projectedValue = CurrentValueSubject<Value, Never>(wrappedValue)
self.projectedValue = _projectedValue.removeDuplicates().eraseToAnyPublisher()
}
}
按预期工作-第三次轻触时以6键触发接收。将EqPublished
替换为Published
并每次调用它
但是,如果我将消费视图更改为
Text("Value is \(model.value)")
.onTapGesture {
// Update every 3rd tap
self.count = (self.count + 1) % 3
self.model.value = self.model.value + (self.count == 0 ? 1 : 0)
}
.onReceive(self.model.objectWillChange) { _ in
print("Model")
}
objectWillChange
publisher永远不会启动,UI也永远不会重新计算-如果我将@EqPublished
更改为普通的@Published
,那么一切正常
问题-更新以加深理解
类如何将自身连接到其发布成员李>ObservedObject
- 什么时候连接?(我认为在
object的访问器中,通过查看标题将改变
)
- 是否可以进行某种反射来检查每个成员并进行连接,而不必关心发布的
的
类型是什么李>值
- 应该坚持一段时间-这在某种程度上是可能的(尽管与
发布的
方式不同) 以下代码更新了上述代码,以包含一个
typeVoid
publisher,以允许相同的流工作。我没有意识到斯威夫特的镜子内置了反射 我不能说我知道valueWillChange
是如何工作的,如果没有一个总体协议,或者没有一个隐藏的发布者来发布一个无效值,或者可能知道如何在不知道ObservedObject
是什么(或者忽略它)的情况下将其作为value
类型发布 这也不难适应可能需要的任何其他条件(小于、值验证等)已发布的
虽然我不想回答实际的问题ObservedObject如何订阅其@Published成员?,但我可以向您展示不同于您想要实现的方法
如果我没有弄错的话,您正在尝试为自定义属性包装类型订阅
publisher。默认情况下,objectWillChange
包装器会自动为您执行此操作。要使用包装器类型,只需手动调用@Published
上的objectWillChange
。因此,您需要订阅您定义的属性包装的发布者(预计值),然后在可用的send()
publisher上调用objectWillChange
方法send()
class Model: ObservableObject { @EqPublished var valueEq = 5 @Published var valueAlways = 5 private var storage = Set<AnyCancellable>() init() { $valueEq .sink { [weak self] _ in self?.objectWillChange.send() } .store(in: &storage) } }
发布者是否会向订阅者发送类模型:ObservableObject{ @eq公布的var值eq=5 @已发布的var值始终=5 私有变量存储=Set() init(){ $valueEq .sink{[弱自我]uu self?.objectWillChange.send() } .store(在:&存储中) } }
对象将更改
和值eq
在使用中的变异?我认为它只打印为值始终
更改而更改的对象,但是valueAlways
的突变不打印。valueEq
和valueAlways
都是valueEq
的上游。objectWillChange
和valueAlways
之间的唯一区别在于抑制重复的数据valueEq
中的值通过valueEq
,以及.removeDuplicates()
可以发送到valueAlways.projectedValue
对象的事实将在无需手动连接或总体协议的情况下更改。连接的自动机制是我感兴趣的,所以我稍微更新了代码以正确执行生命周期-
应该在实际更改发生之前触发EqPublished::valueWillChange
-这反映在objectWillChange
中。还删除了didSet
,通过简单的相等性检查将它们保持在同一个位置,而不是真正的问题所在-removeDuplicates
建立了@Published
和Published::projectedValue
之间的关系,这就是我正在尝试的实现。这需要某种类型的反射来完成,而不需要硬编码(如前所述,我有数百个,接近数千个基于平等的连接要做——硬编码每一个都不太好)ObservedObject::objectWillchange
class Model: EqObservableObject { @EqPublished var valueEq = 5 @Published var valueAlways = 5 } @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) public class EqObservableObject : Combine.ObservableObject { private var eqValuesChanging: [AnyCancellable] = [] init() { let mirror = Mirror(reflecting: self) for child in mirror.children { if let value = child.value as? EqPublishedProtocol { eqValuesChanging.append( value.valueWillChange.sink(receiveValue: { [weak self] in self?.objectWillChange.send() }) ) } } } } public protocol EqPublishedProtocol { var valueWillChange: AnyPublisher<Void, Never> { get } } @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) @propertyWrapper public struct EqPublished<Value: Equatable>: EqPublishedProtocol { public var wrappedValue: Value { willSet { if wrappedValue != newValue { _valueWillChange.send() } } didSet { if oldValue != wrappedValue { _projectedValue.send(wrappedValue) } } } public var projectedValue: AnyPublisher<Value, Never> public var valueWillChange: AnyPublisher<Void, Never> private var _projectedValue: CurrentValueSubject<Value, Never> private var _valueWillChange: CurrentValueSubject<Void, Never> /// Initialize the storage of the Published property as well as the corresponding `Publisher`. public init(wrappedValue: Value) { self.wrappedValue = wrappedValue self._projectedValue = CurrentValueSubject<Value, Never>(wrappedValue) self.projectedValue = _projectedValue.eraseToAnyPublisher() self._valueWillChange = CurrentValueSubject<Void, Never>() self.valueWillChange = self._valueWillChange.eraseToAnyPublisher() } }
HStack { Text("ValueEq is \(model.valueEq)") .onTapGesture { self.count = (self.count + 1) % 3 self.model.valueEq = self.model.valueEq + (self.count == 0 ? 1 : 0) } .onReceive(self.model.$valueEq) { val in print("ValueEq is \(val)") } Text("ValueAlways is \(model.valueAlways)") .onTapGesture { self.count = (self.count + 1) % 3 self.model.valueAlways = self.model.valueAlways + (self.count == 0 ? 1 : 0) } .onReceive(self.model.$valueAlways) { val in print("ValueAlways is \(val)") } } .onReceive(self.model.objectWillChange) { _ in print("Object Changed") }
class Model: ObservableObject { @EqPublished var valueEq = 5 @Published var valueAlways = 5 private var storage = Set<AnyCancellable>() init() { $valueEq .sink { [weak self] _ in self?.objectWillChange.send() } .store(in: &storage) } }