将协议和符合类(!)实例作为参数

时间:2016-06-08 15:58:46

标签: swift generics swift-protocols

我试图找出如何定义一个具有以下功能的函数 两个参数:

  1. 协议。
  2. 符合该协议的引用类型)的实例。
  3. 例如,给定

    protocol P { }
    class C : P { } // Class, conforming to P
    class D { }     // Class, not conforming to P
    struct E: P { } // Struct, conforming to P
    

    这应该编译:

    register(P.self, obj: C()) // (1)
    

    但这些不应该编译:

    register(P.self, obj: D()) // (2)  D does not conform to P
    register(P.self, obj: E()) // (3)  E is not a class
    

    如果我们放弃第二个参数是类实例的条件,那么很容易:

    func register<T>(proto: T.Type, obj: T) {
        // ...
    }
    

    但这也会接受(3)中的struct(值类型)。 这看起来很有希望并且编译

    func register<T: AnyObject>(proto: T.Type, obj: T) {
        // ...
    }
    

    但是(1)(2)(3)都不再编译,例如。

    register(P.self, obj: C()) // (1)
    // error: cannot invoke 'register' with an argument list of type '(P.Protocol, obj: C)'
    

    假设编译器错误的原因与中的相同 Protocol doesn't conform to itself?

    另一次失败的尝试是

    func register<T>(proto: T.Type, obj: protocol<T, AnyObject>) { }
    // error: non-protocol type 'T' cannot be used within 'protocol<...>'
    

    一个可行的替代方案是一个以参数

    为参数的函数
    1. class 协议。
    2. 符合该协议的类型的实例。
    3. 这里的问题是如何限制第一个参数 类协议被接受。

      背景:我最近偶然发现了 SwiftNotificationCenter 实现面向协议的类型安全通知机制的项目。 它有一个 register 看起来像这样的方法:

      public class NotificationCenter {
      
          public static func register<T>(protocolType: T.Type, observer: T) {
              guard let object = observer as? AnyObject else {
                  fatalError("expecting reference type but found value type: \(observer)")
              }
      
              // ...
          }
      
          // ...
      }
      

      然后将观察者存储为弱引用,这就是他们的原因 必须是引用类型,即类的实例。 但是,仅在运行时检查,我想知道如何使其成为编译时检查。

      我错过了一些简单/明显的东西吗?

1 个答案:

答案 0 :(得分:0)

你无法直接做你想做的事。它与引用类型无关,因为任何约束都会使P.self: P.Protocol存在,因此当您引用协议的元类型时,无法在呼叫站点满足它们。 {1}}和采用者C。有一种特殊情况,T不受限制,允许它首先工作。

到目前为止,更常见的情况是约束T: P并要求P: class,因为您可以使用任意协议的元类型执行事项将名称转换为字符串。它恰好在这种狭隘的情况下很有用,但就是这样;签名也可能是register<T>(proto: Any.Type, obj: T)所有的好处。

理论上,Swift可以支持对元类型的约束,register<T: AnyObject, U: AnyProtocol where T.Type: U>(proto: U, obj: T)但是我怀疑它在许多情况下会有用。