从任何一个Swift失败演员?协议

dea*_*rne 18 casting swift

仅供参考:Swift bug在这里提出:https://bugs.swift.org/browse/SR-3871


我有一个奇怪的问题,演员表不能正常工作,但控制台显示它是正确的类型.

我有一个公共协议

public protocol MyProtocol { }
Run Code Online (Sandbox Code Playgroud)

我在一个模块中实现它,使用返回实例的公共方法.

internal struct MyStruct: MyProtocol { }

public func make() -> MyProtocol { return MyStruct() }
Run Code Online (Sandbox Code Playgroud)

然后,在我的视图控制器中,我触发一个带有该对象的segue作为发送者

let myStruct = make()
self.performSegue(withIdentifier: "Bob", sender: myStruct)
Run Code Online (Sandbox Code Playgroud)

到目前为止都很好.

问题出在我的prepare(for:sender:)方法中.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "Bob" {
        if let instance = sender as? MyProtocol {
            print("Yay")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,实例转换为MyProtocol始终返回nil.

当我po sender as! MyProtocol在控制台中运行时,它给了我错误Could not cast value of type '_SwiftValue' (0x1107c4c70) to 'MyProtocol' (0x1107c51c8).但是,po sender将输出有效的Module.MyStruct实例.

为什么这个演员不起作用?

(我已经设法通过在一个结构中装载我的协议来解决它,但我想知道为什么它不能正常工作,如果有更好的方法来解决它)

Ham*_*ish 24

这是一个非常奇怪的错误 - 它似乎发生在一个实例通过盒装在一个被桥接到Obj-C _SwiftValue并且被静态类型化时发生的Any(?).然后,该实例不能转换为它符合的给定协议.

据Joe Groff在您提交错误报告的评论中提到:

这是一般的"运行时动态转换不会桥接,如果有必要桥接到协议"错误的实例.由于发送者被视为_SwiftValue对象类型,并且我们试图达到它不符合的协议,我们放弃而不尝试桥接类型.

一个更小的例子是:

protocol P {}
struct S : P {}

let s = S()

let val : Any = s as AnyObject // bridge to Obj-C as a _SwiftValue.

print(val as? P) // nil
Run Code Online (Sandbox Code Playgroud)

奇怪的是,首先转换为AnyObject然后转换为协议似乎工作:

print(val as AnyObject as! P) // S()
Run Code Online (Sandbox Code Playgroud)

所以看起来静态输入它AnyObject使Swift也检查了桥接类型的协议一致性,允许强制转换成功.正如Joe Groff在另一篇评论中所解释的那样,其原因是:

运行时有一些错误,它只尝试某些转换到一个深度级别,但不执行其他转换后(因此AnyObject - > bridge - > Protocol可能有效,但Any - > AnyObject - > bridge - > Protocol doesn "T).它无论如何都应该起作用.