我试图找出如何定义一个具有以下功能的函数
两个参数:
两个参数:
>协议.
>符合该协议的类(引用类型)的实例.
例如,给定
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<...>'
可行的替代方案是作为参数的函数
>类协议.
>符合该协议的类型的实例.
这里的问题是如何限制第一个参数
类协议被接受.
背景:我最近偶然发现了
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)") } // ... } // ... }
然后观察者被存储为弱引用,这就是他们的原因
必须是引用类型,即类的实例.
但是,仅在运行时检查,我想知道如何使它成为编译时检查.
我错过了一些简单/明显的东西吗?
你无法直接做你想做的事.它与引用类型无关,这是因为任何约束都会使T存在,因此当您引用协议的元类型P.self:P.Protocol和采用者C时,不可能在调用站点满足它们.特殊情况,当T不受约束,允许它首先工作.
到目前为止,更常见的情况是约束T:P并且需要P:class,因为对于任意协议的元类型,你唯一可以做的就是将名称转换为字符串.它恰好在这种狭隘的情况下很有用,但就是这样;签名也可以注册< T>(proto:Any.Type,obj:T),它将做的所有好处.
从理论上讲,Swift可以支持约束元类型,ala寄存器< T:AnyObject,U:AnyProtocol其中T.Type:U>(proto:U,obj:T)但我怀疑它在许多情况下会有用.