我正在尝试找出如何定义一个函数,该函数接受以下两个参数:
- 协议。
- 符合该协议的一个类(引用类型)的实例。
例如,给定
protocol P { }
class C : P { } // Class, conforming to P
class D { } // Class, not conforming to P
struct E: P { } // Struct, conforming to P
这应该可以编译:
register(proto: P.self, obj: C()) // (1)
但是这些代码不应该编译通过:
register(proto: P.self, obj: D()) // (2) D does not conform to P
register(proto: P.self, obj: E()) // (3) E is not a class
如果我们不考虑第二个参数是类实例的条件,那么这就很容易了:
func register<T>(proto: T.Type, obj: T) {
// ...
}
但这也会接受结构(值类型)在(3)
中的参数。看起来很有前途并且可以编译。
func register<T: AnyObject>(proto: T.Type, obj: T) {
// ...
}
但此时 (1)
、(2)
、(3)
皆无法编译,例如:
register(proto: P.self, obj: C()) // (1)
// error: cannot invoke 'register' with an argument list of type '(P.Protocol, obj: C)'
我假设编译器错误的原因与协议不符合自身中的相同。
另一个失败的尝试是:
func register<T>(proto: T.Type, obj: protocol<T, AnyObject>) { }
// error: non-protocol type 'T' cannot be used within 'protocol<...>'
一个可行的替代方案是一个函数,它以以下参数为输入:
- 一个类协议。
- 符合该协议的类型的实例。
背景:最近我偶然发现了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)")
}
// ...
}
// ...
}
观察者被存储为弱引用,因此它们必须是引用类型,即类的实例。然而,这只有在运行时才会被检查,我想知道如何将其变成编译时检查。
我是否漏掉了一些简单/明显的东西?
protocol X: class {}
是否会让这个更加安全呢? - sschaleT
将是P
,这不是一个具体类型。我还注意到协议关联类型有类似的行为(一旦你对它们进行约束,它们只能采用具体类型) - 实际上有一个关于此的错误报告。 - Hamish