鉴于两个类A
和PC
以及协议P
:
@objc public protocol P {} // fun fact on the side: without @objc, swiftc crashes
public class PC : P {}
public class A<T:P> {
public init() { }
public func test() -> Void {
let pc = PC()
let t = pc as T // this cast leads to the error mentioned below
}
}
这些编译正常。
现在我想这样打电话给test()
:
let a = A<P>() // parametrized with the *protocol* P
a.test()
这也很好地编译。
但是,当我运行它并尝试强制转换为T
(通过pc as T
)时,调试器会进入:
swift_dynamicCastUnknownClassUnconditional:
[...]
0x105fedf76: leaq 0x3680f(%rip), %rax ; "bad metadata kind!"
为什么会这样?
我假设因为T
受P
约束且PC
采用P
,所以PC
每个T
也必须为let a = A<PC>() // parametrized with the *class*, not the protocol
a.test()
。
其他信息 - 这:
{{1}}
就像魅力一样。
答案 0 :(得分:0)
我并不感到惊讶这个代码是有问题的,即使它似乎应该在运行时工作,你应该重构它以不同的方式解决问题。这可能是值得关注的,但我认为这是值得商榷的。它归结为我不相信这一行:
let t = pc as T
是有效的事情。 as
正在说“无论pc
是什么,我想将其设为T
类型。”但是不能保证这没关系。以下是什么:
extension String: P { }
let x = A<String>()
以上是完全可以接受的声明 - String
符合P
,因此您可以创建A
,其中T
为String
。但是你不能写let pc = PC(); let t = pc as String
- PC
和String
完全不相关的类型。如果您尝试在非通用代码中执行此操作,则会出现编译器错误。对于通用版本,你有点像一个地狱世界 - 通用函数应该就像说“在编译时,假设T
被实际类型替换,给我写一个就像那样的函数” 。目前还不清楚在这种情况下应该如何解释as T
- 它可能在编译时失败,也可能不会。
现在你可能会争辩说“没关系,我总是会确保在我打电话时创建我的课程我只会使用可以工作的类型。它永远不会崩溃,因为我永远不会用某些东西来调用它崩溃”。在你的例子中就是这种情况。
但这不是Swift泛型操作的方式--Swift尽力确保泛型内部发生的事情总是对任何符合通用标准的类型有效。只是因为,在你在这里调用它的方式,它碰巧工作,并且你已经说服Swift让它编译不会使代码有效,因此,你观察到的行为对我来说似乎是合理的。