致命错误:无法从Objective-C桥接数组 - 为什么你还要尝试,Swift?

92

我已经声明了一个Swift协议:

protocol Option {
    var name: String { get }
}

我声明了这个协议的多个实现——一些是类,一些是枚举。

我有一个视图控制器,其属性声明如下:

var options: [Option] = []

当我尝试在另一个视图控制器的 prepareForSegue 中将该属性设置为实现了 Option 协议的对象数组时,我会收到运行时错误:

fatal error: array cannot be bridged from Objective-C
为什么这个不起作用?编译器已经有了所有需要的信息,我不理解这与Objective-C有什么关系——我的项目只包含Swift文件,并且这些数组没有进出任何必须被桥接到NSArray的框架方法中。

6
你尝试过在协议前添加 @objc 吗?https://dev59.com/gWAf5IYBdhLWcg3w9Wiu#28029568 - Fabio Poloni
1
如果任何一种协议实现是枚举类型,则无法使用该方法:“非类类型“Foo”无法符合类协议“Option”。 - Robert Atkins
为什么必须是类协议呢?我没有将它传递给 Obj-C 框架或任何其他需要将 Swift 数组桥接到 NSArray 的东西。 - Robert Atkins
Swift 和 Objective-C 如何协同工作仍然是个谜。我只能“接受”许多东西,它们要么“起作用”,要么“不起作用”。 - Fabio Poloni
9
为什么这个问题有这么多踩?在我看来,它是一个公正清晰的问题。 - Guven
显示剩余3条评论
4个回答

83

我已经找到了一个解决方案。它非常……不令人满意,但它有效。在我设置目标视图控制器上的数组时,我这样做:

我找到了一个解决方案。虽然它让人感觉不太好,但它可以起作用。当我在目标视图控制器上设置数组时,我执行以下操作:

destinationViewController.options = options.map({$0 as Option})

你不能将整个数组强制转换吗? options 作为 [Option] - Kostiantyn Koval
不行。我试过了(Xcode 6.3.1(6D1002)),不起作用。无论如何,我都不应该需要强制转换它,编译器知道我正在传递实现Option的事物数组。 - Robert Atkins
2
“一个实现Option的事物数组”,但这并不等同于你所需要的Option数组。请看我的回答。 - matt
1
这个可以工作,但是很不令人满意...这不应该是必需的。Swift 应该能够处理这个。 - Oscar Gomez
我同意...它确实是这样工作的,但这是一段非常不令人满意的代码。 - Michael

22

编译器知道我传递了一个实现Option的东西数组

你透露了一个非常具有启示性的话,这表明了问题的根源。"实现Option的东西数组"不是一个Option数组。

问题出在你创建options的地方(在prepareForSegue中)。虽然你没有展示那段代码,但我敢打赌你在那个地方没有进行强制类型转换/定义类型。这就是为什么赋值失败的原因。options可能是一些确实采用了Option协议的东西的数组,但这还不够,它必须被定义为一个Option数组。

因此,在prepareForSegue中,按照以下方式形成options

let options : [Option] = // ... whatever ...

现在您将能够直接将其分配给 destinationViewController.options

这是一个快速的测试用例(在游乐场中,我不喜欢游乐场,但它们有它们的用途):

protocol Option {
    var name : String {get}
}

class ViewController : UIViewController {
    var options : [Option] = []
}

enum Thing : Option {
    var name : String {
        get {
            return "hi"
        }
    }
    case Thing
}

let vc = ViewController()
let options : [Option] = [Thing.Thing]
vc.options = options // no problem

我还在一个真实的应用程序中测试了这个并且使用了一个真实的prepareForSegue,结果它工作得很好。


1
我认为这个极其糟糕,因为编译器在运行时确实知道Thing是一个Option。而且无论如何,正如我在下面自己的答案评论中指出的那样,无论是强制转换(viewController.options = things as [Option])还是像你在这里建议的显式声明类型为[Option]的临时变量,都不起作用。在两种情况下,我都会得到运行时错误。 - Robert Atkins
那么你必须解释为什么它对我有效。还有其他事情发生了,你没有说明。如果你不透露更多的代码,我只能怀疑你隐瞒了一些重要的东西。 - matt
也许吧。但我仍然困惑于这与Objective-C一开始有什么关系(即原始运行时错误)。我没有做任何(我能看到的)应该强制转换为NSArray的桥接转换。 - Robert Atkins
2
就这么看吧。我已经展示了可行的代码。你并没有给我展示不可行的代码——我无法通过所提供的数据重现你的问题。请帮我重现它。 - matt
1
@CristiBăluță 在声称“这个问题仍未解决”之前,您需要找出这一点。 - matt
显示剩余9条评论

16

我之前也遇到过同样的问题,我通过在协议上标记 @objc 来解决它。在你的情况下,代码应该是这样的:

@objc protocol Option {
    var name: String { get }
}

这个答案中找到了解决方案。


1
正如原问题的评论所述,如果协议的任何实现者是Swift枚举,则此方法无法使用。而在我的情况下,它们就是。 - Robert Atkins
笔误:obcj 应为 objc。 - Alan Scarpa

1

这一个也正常工作

destinationViewController.options = options.map{$0}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接