Mar*_*n R 6 generics swift swift-protocols
我试图弄清楚如何定义一个采用以下两个参数的函数:
例如,给定
protocol P { }
class C : P { } // Class, conforming to P
class D { } // Class, not conforming to P
struct E: P { } // Struct, conforming to P
Run Code Online (Sandbox Code Playgroud)
这应该编译:
register(P.self, obj: C()) // (1)
Run Code Online (Sandbox Code Playgroud)
但这些不应该编译:
register(P.self, obj: D()) // (2) D does not conform to P
register(P.self, obj: E()) // (3) E is not a class
Run Code Online (Sandbox Code Playgroud)
如果我们放弃第二个参数是类实例的条件,这很容易:
func register<T>(proto: T.Type, obj: T) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
但是这也会接受struct(值类型)(3).这看起来很有希望和编译
func register<T: AnyObject>(proto: T.Type, obj: T) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
但随后没有的(1),(2),(3)编译了,如
register(P.self, obj: C()) // (1)
// error: cannot invoke 'register' with an argument list of type '(P.Protocol, obj: C)'
Run Code Online (Sandbox Code Playgroud)
我假设编译器错误的原因与Protocol中的相同 并不符合自身的原因?.
另一次失败的尝试是
func register<T>(proto: T.Type, obj: protocol<T, AnyObject>) { }
// error: non-protocol type 'T' cannot be used within 'protocol<...>'
Run Code Online (Sandbox Code Playgroud)
可行的替代方案是作为参数的函数
这里的问题是如何限制第一个参数,以便只接受类协议.
背景:我最近偶然发现了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)")
}
// ...
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
然后将观察者存储为弱引用,这就是为什么它们必须是引用类型,即类的实例.但是,仅在运行时检查,我想知道如何使它成为编译时检查.
我错过了一些简单/明显的东西吗?
您无法直接做您想做的事情。它与引用类型无关,因为任何约束都是存在的,因此当您引用协议的元类型和采用者T时,不可能在调用站点满足它们。有一种特殊情况,当不受约束时,它首先可以工作。P.self: P.ProtocolCT
到目前为止,更常见的情况是约束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)但我怀疑它在许多场景中是否有用。