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的访问器中,通过查看标题将改变
  • 是否可以进行某种反射来检查每个成员并进行连接,而不必关心发布的
    类型是什么
      应该坚持一段时间-这在某种程度上是可能的(尽管与
      发布的
      方式不同)

      以下代码更新了上述代码,以包含一个
      Void
      type
      valueWillChange
      publisher,以允许相同的流工作。我没有意识到斯威夫特的镜子内置了反射

      我不能说我知道
      ObservedObject
      是如何工作的,如果没有一个总体协议,或者没有一个隐藏的发布者来发布一个无效值,或者可能知道如何在不知道
      value
      是什么(或者忽略它)的情况下将其作为
      已发布的
      类型发布

      这也不难适应可能需要的任何其他条件(小于、值验证等)


      虽然我不想回答实际的问题ObservedObject如何订阅其@Published成员?,但我可以向您展示不同于您想要实现的方法


      如果我没有弄错的话,您正在尝试为自定义属性包装类型订阅
      objectWillChange
      publisher。默认情况下,
      @Published
      包装器会自动为您执行此操作。要使用包装器类型,只需手动调用
      objectWillChange
      上的
      send()
      。因此,您需要订阅您定义的属性包装的发布者(预计值),然后在可用的
      objectWillChange
      publisher上调用
      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)
          }
      }