使用协议参数化的​​通用Swift类在运行时失败。为什么?

时间:2015-01-30 15:55:46

标签: swift generics

鉴于两个类APC以及协议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!"

为什么会这样?

我假设因为TP约束且PC采用P,所以PC每个T也必须为let a = A<PC>() // parametrized with the *class*, not the protocol a.test()

其他信息 - 这:

{{1}}

就像魅力一样。

1 个答案:

答案 0 :(得分:0)

我并不感到惊讶这个代码是有问题的,即使它似乎应该在运行时工作,你应该重构它以不同的方式解决问题。这可能是值得关注的,但我认为这是值得商榷的。它归结为我不相信这一行:

let t = pc as T

是有效的事情。 as正在说“无论pc是什么,我想将其设为T类型。”但是不能保证这没关系。以下是什么:

extension String: P { }
let x = A<String>()

以上是完全可以接受的声明 - String符合P,因此您可以创建A,其中TString。但是你不能写let pc = PC(); let t = pc as String - PCString完全不相关的类型。如果您尝试在非通用代码中执行此操作,则会出现编译器错误。对于通用版本,你有点像一个地狱世界 - 通用函数应该就像说“在编译时,假设T被实际类型替换,给我写一个就像那样的函数” 。目前还不清楚在这种情况下应该如何解释as T - 它可能在编译时失败,也可能不会。

现在你可能会争辩说“没关系,我总是会确保在我打电话时创建我的课程我只会使用可以工作的类型。它永远不会崩溃,因为我永远不会用某些东西来调用它崩溃”。在你的例子中就是这种情况。

但这不是Swift泛型操作的方式--Swift尽力确保泛型内部发生的事情总是对任何符合通用标准的类型有效。只是因为,在你在这里调用它的方式,它碰巧工作,并且你已经说服Swift让它编译不会使代码有效,因此,你观察到的行为对我来说似乎是合理的。