在 Swift 中从 Any 转换失败?协议 [英] Failing cast in Swift from Any? to protocol

查看:25
本文介绍了在 Swift 中从 Any 转换失败?协议的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

仅供参考:此处提出 Swift 错误:https://bugs.swift.org/browse/SR-3871

<小时>

我遇到了一个奇怪的问题,即演员表不起作用,但控制台将其显示为正确的类型.

我有一个公共协议

公共协议 MyProtocol { }

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

internal struct MyStruct: MyProtocol { }public func make() ->MyProtocol { 返回 MyStruct() }

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

let myStruct = make()self.performSegue(withIdentifier: "Bob", 发件人: myStruct)

到目前为止一切都很好.

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

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {if segue.identifier == "Bob" {if let instance = sender as?我的协议{打印(耶")}}}

然而,实例到 MyProtocol 的转换总是返回 nil.

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

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

(我已经设法通过将我的协议装箱到一个结构体中来解决它,但我想知道为什么它不能按原样工作,以及是否有更好的方法来解决它)

解决方案

这是一个非常奇怪的错误 – 看起来它发生在一个实例通过在 _SwiftValue 并且静态类型为 Any(?).然后,该实例无法转换为它所遵循的给定协议.

根据 Joe Groff 在您提交的错误报告的评论中所说:><块引用>

这是一般运行时动态转换在必要时不会桥接到协议"错误的一个实例.由于发送方被视为 _SwiftValue 对象类型,而我们正在尝试使用它不符合的协议,因此我们放弃而不尝试桥接类型.

一个更简单的例子是:

protocol P {}结构 S : P {}让 s = S()let val : Any = s as AnyObject//作为 _SwiftValue 连接到 Obj-C.打印(val as?P)//nil

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

print(val as AnyObject as!P)//S()

因此,将其静态键入为 AnyObject 似乎使 Swift 还检查桥接类型的协议一致性,从而使转换成功.正如 Joe Groff 在另一条评论中所解释的,这样做的原因是:

<块引用>

运行时有许多错误,它只尝试将某些转换转换到一个深度级别,但在执行其他转换后不会尝试(因此 AnyObject -> 桥 -> 协议可能有效,但 Any -> AnyObject -> 桥 -> 协议没有).无论如何,它应该工作.

FYI: Swift bug raised here: https://bugs.swift.org/browse/SR-3871


I'm having an odd problem where a cast isn't working, but the console shows it as the correct type.

I have a public protocol

public protocol MyProtocol { }

And I implement this in a module, with a public method which return an instance.

internal struct MyStruct: MyProtocol { }

public func make() -> MyProtocol { return MyStruct() }

Then, in my view controller, I trigger a segue with that object as the sender

let myStruct = make()
self.performSegue(withIdentifier: "Bob", sender: myStruct)

All good so far.

The problem is in my prepare(for:sender:) method.

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

However, the cast of instance to MyProtocol always returns nil.

When I run po sender as! MyProtocol in the console, it gives me the error Could not cast value of type '_SwiftValue' (0x1107c4c70) to 'MyProtocol' (0x1107c51c8). However, po sender will output a valid Module.MyStruct instance.

Why doesn't this cast work?

(I've managed to solve it by boxing my protocol in a struct, but I'd like to know why it's not working as is, and if there is a better way to fix it)

解决方案

This is pretty weird bug – it looks like it happens when an instance has been bridged to Obj-C by being boxed in a _SwiftValue and is statically typed as Any(?). That instance then cannot be cast to a given protocol that it conforms to.

According to Joe Groff in the comments of the bug report you filed:

This is an instance of the general "runtime dynamic casting doesn't bridge if necessary to bridge to a protocol" bug. Since sender is seen as _SwiftValue object type, and we're trying to get to a protocol it doesn't conform to, we give up without also trying the bridged type.

A more minimal example would be:

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

Weirdly enough, first casting to AnyObject and then casting to the protocol appears to work:

print(val as AnyObject as! P) // S()

So it appears that statically typing it as AnyObject makes Swift also check the bridged type for protocol conformance, allowing the cast to succeed. The reasoning for this, as explained in another comment by Joe Groff, is:

The runtime has had a number of bugs where it only attempts certain conversions to one level of depth, but not after performing other conversions (so AnyObject -> bridge -> Protocol might work, but Any -> AnyObject -> bridge -> Protocol doesn't). It ought to work, at any rate.

这篇关于在 Swift 中从 Any 转换失败?协议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆