【问题标题】:How to define a Swift protocol that enforces its adopters themselves to conform to an associated type?如何定义一个强制其采用者自己遵守关联类型的 Swift 协议?
【发布时间】:2018-03-02 17:37:00
【问题描述】:

我需要处理的对象不仅要符合协议,还要公开它们所遵循的 second 协议的类型。 (这适用于 NSXPCConnection,您不仅必须配置要代理的对象,还必须告诉它应该在该代理对象上公开哪个协议。)

我尝试了类似的方法:

protocol Conformer where Self : Conformer.P  {
    associatedtype P : Protocol
    static var interface : P {get}
    init(info: String)
}

func exposeOverXPC<T:Conformer>(_ _: T.Type) {
    let c : NSXPCConnection = …

    c.exportedInterface = NSXPCInterface(with: T.interface)
    c.exportedObject = T(info:"foo")
}

但是会导致错误:

关联类型“P”只能与具体类型或泛型参数库一起使用

特别是我希望exposeOverXPC 只接受以下对象:

  1. 可以以特定方式初始化
  2. 拥有引用协议的静态属性interface
  3. 本身是否符合上述interface

这是我卡住的最后一步,有什么办法可以完成吗?

【问题讨论】:

  • 删除协议上的where 子句应该使代码可编译。它在你的设计中应该扮演什么角色?
  • 另请注意,您不能真正符合 Protocol 实例,因为它实际上不是协议,它主要包含有关 Objective-C 协议的信息。
  • where 子句是(失败的)尝试强制执行我的#3 标准,确保类实际上符合它公开的接口。您可能已经找到了问题的核心,即 Swift 不喜欢针对 @interface Protocol : NSObject 的实例测试一致性。有没有不同的方法来实现我的三个标准?
  • 我的直觉告诉我这是不可能的。 ObjC 中的协议一致性与 Swift 中的非常不同,Protocolprotocol 几乎没有关系。出于 XPC 的目的,您绝对可以遵守所需的协议,而无需正式声明该事实(过去通过“非正式协议”真正很常见,尽管现在不那么如此了)。您只需要在运行时响应所需的方法。这与 Swift 的协议理念完全不同。这是一个有趣的问题,但我不知道编译时强制是否可以解决。

标签: swift generics swift-protocols


【解决方案1】:

您不能限制谁符合协议,如果您考虑一下,这与首先拥有协议的概念背道而驰。但是,您可以在 exposeOverXPC 的泛型参数中使用组合类型、Swift4 功能。

protocol Interface {
}

protocol XPCExposable {
    associatedtype P: Interface

    init(info: String)
    static var interface: P { get }
}

func exposeOverXPC<T: XPCExposable & Interface>(_ : T.Type) {
    // 1: T is initializeable in a particular way
    // 2: Has a static property interface which references a protocol
    // 3: Are themselves conformant to said interface
}

是的,此约束 T 符合 Interface 而不是 P,最好的办法是使 exposeOverXPC 私有/内部并提供期望 Interface 子类型的 APIs。无论您在哪里可以访问 Interface 子类型,都可以公开该 API。例如:

解决方案 1

protocol InterfaceSubType: Interface {
    fun test()
}

/// Create as many `API`s as the number of `Interface` subtypes you have.
func exposeOverXPC<T: XPCExposable & InterfaceSubType>(_ : T.Type) {
    exposeOverXPC(T.self)
}

/// set to private, you only want to expose the APIs with `Interface` subtype.
private func exposeOverXPC<T: XPCExposable & Interface>(_ : T.Type) {
    // Impl.
}

解决方案 2

具有参数类型为关联类型的函数的另一种解决方案是通过扩展协议添加该 api(如果您愿意,作为静态函数)。您必须知道此扩展中 Interface 的所有预期子类型。

extension XPCExposable {

    static func exposeOverXPC<T>(_ interface: P, _ xpcType: T.Type) where T: XPCExposable {

        // Expected subtype Interface 
        if let subInterface = interface as? InterfaceSubType {
            subInterface.test()
        }

        // Other subtypes here.
    }
}

可以称为:

let impl = Impl(info: "")
Impl.exposeOverXPC(Impl.interface, Impl.self)

它是XPCExposable 的扩展,因此您可以将调用者限制为conformer,并且参数需要XPCExposable.P,因此您已准备就绪。 此解决方案的缺点是:

  1. 您有两个参数而不是一个。
  2. 它使用if 条件,我不知道这是否值得一提,除了我想将第一个解决方案推向最喜欢。

【讨论】:

  • 我认为这只要求它符合 some Interface 协议,而不是具体的P
  • 是的,我很确定你不能那样做。我什至想不出符合相关协议P 的样子。无论如何,我已经用一些解决方案编辑了我的答案,以在Interface 上添加更多约束。
猜你喜欢
  • 1970-01-01
  • 2014-08-18
  • 1970-01-01
  • 1970-01-01
  • 2020-11-14
  • 1970-01-01
  • 1970-01-01
  • 2020-04-16
  • 1970-01-01
相关资源
最近更新 更多