【问题标题】:How do I make an Observer pattern with 2 Swift protocols, where the two associatedtypes must be the same?如何使用 2 个 Swift 协议创建观察者模式,其中两个关联类型必须相同?
【发布时间】:2020-08-29 22:17:25
【问题描述】:

我正在尝试使用SE-0142 (Associated Type Constraints) 来创建具有 2 个协议的观察者模式,IsObserver(如客户端)和 HasObservers(如服务器),其中有一个共享的 DataType 表示类型被观察的事物。

我需要符合HasObservers 的对象才能成为结构或类,并且我希望有意将IsObserver 限制为类(想要,但不需要)。

我不擅长泛型......几个小时后,我走到了这一步,编译器错误在下面的内联评论中。我被困住了,不知道下一步该去哪里,而且我不确定这种方法是否可行或合理。非常感谢所有帮助!

import Foundation

protocol IsObserver: class {
    associatedtype DataType
    func dataDidUpdate(_ data: [DataType])
}

struct Observation<T: IsObserver> {
    weak var observer: T?
}


protocol HasObservers {
    associatedtype DataType : IsObserver where DataType.DataType == DataType
    static var observations: [ObjectIdentifier : Observation<IsObserver>] { get set } // ERROR: "Value of protocol type 'IsObserver' cannot conform to 'IsObserver'; only struct/enum/class types can conform to protocols"
    static func tellObserversDataDidUpdate(_ data: [DataType])
}

extension HasObservers {
    static func tellObserversDataDidUpdate(_ data: [DataType]) {
        for (id, observation) in observations {
            guard let observer = observation.observer else {
                observations.removeValue(forKey: id)
                continue
            }
            observer.dataDidUpdate(data)
        }
    }

    static func addObserver<T: IsObserver>(_ observer: T) {
        let id = ObjectIdentifier(observer)
        let ob = Observation.init(observer: observer)
        observations[id] = ob
    }

    static func removeObserver<T: IsObserver>(_ observer: T) {
        let id = ObjectIdentifier(observer)
        observations.removeValue(forKey: id)
    }
}

UPDATE:好的,终于到了。比我想象的要难,需要类型擦除。在这个要点中,有两个版本:第一个版本是根据原始问题具有相关类型协议的版本。但它是有限的——作为观察者的对象只能观察一种类型。所以我做了另一个变种,它可以有多种类型但不使用 associatetype 协议,所以观察者必须手动检查类型。

https://gist.github.com/xaphod/4f8a6402429759b6b3fd8ea2d8ea53c4

【问题讨论】:

  • @Jessy 目光短浅:我们大多数人都有需要支持的专业应用程序,当然在某些情况下必须一直支持到 iOS 9。另外,Combine 不是 Apple 特有的吗?如果我想在服务器上这样做呢?
  • @Jessy 嗯,你可能是个业余爱好者,但我们中的一些人以此为生。专业领域对 iOS12 的支持不是可选的
  • @xaphod 观察与观察有什么用?也许我没有关注
  • @NewDev 我不确定我是否理解你的问题,你的意思是为什么struct Observation 存在?这是因为我想对观察者有一个弱内存引用,以防它是一个需要 deinit 的类

标签: swift protocols associated-types


【解决方案1】:

我会稍微简化一下你的用例(忽略观察),希望能理解这个概念。

HasObservers 基本上有 2 个关联类型 - DataTypeIsObserver 类型,然后您将限制 IsObserver 类型以拥有正确的 DataType

protocol IsObserver {
  associatedtype DataType
  func dataDidUpdate(_ data: [DataType])
}

protocol HasObservers {
  associatedtype DataType
  associatedtype ObserverType: IsObserver where ObserverType.DataType == DataType

  static func addObserver(_ observer: ObserverType)
  static func tellObserversDataDidUpdate(_ data: [DataType])

  // ..
}

【讨论】:

  • 这确实帮助我走得更远(谢谢!),但是,拥有额外的关联类型似乎意味着符合HasObservers 的结构/类必须预先知道它的观察者将使用什么类型是。这是我不想要的。例如:如何在 HasObserver 中声明保存观察者的 var,用这种方法?
  • @xaphod - 我明白了,所以是的 - 当你为每个 DataType (例如 HasStringObserversStringObserver)都有一个特定的观察者实现时,这很有效。如果对于给定的数据类型(例如 StringObserverAStringObserverB)有不同的 IsObserver 实现,那么您需要使用类型擦除模式。你可以在这里阅读更多信息:swiftbysundell.com/articles/…
  • 类型擦除要困难得多,但我最终做到了。我正在使用指向要点的链接更新我上面的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 2021-09-20
  • 1970-01-01
  • 2020-12-26
  • 2013-12-03
  • 1970-01-01
相关资源
最近更新 更多