“致命错误: 数组无法与 Objective-C 连接”ーー你为什么还要尝试呢,斯威夫特?

我已经宣布了斯威夫特协议:

protocol Option {
var name: String { get }
}

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

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

var options: [Option] = []

当我尝试将这个属性设置为在另一个 VC 的 prepareForSegue中实现 Option协议的对象数组时,我得到一个运行时错误:

fatal error: array cannot be bridged from Objective-C

为什么这个不管用?编译器拥有它所需要的所有信息,而我完全不知道 Objective-C 和它有什么关系ーー我的项目只包含 Swift 文件,而且这些数组不会进出任何框架方法,因为这些方法需要将它们连接到 NSArray

12606 次浏览

I have found a solution. It is quite... unsatisfying, but it works. Where I set the array on the destination view controller I do:

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

the compiler knows I'm passing in an Array of things that implement Option

You've let slip there a very revealing remark, which suggests the source of the issue. An "Array of things that implement Option" is not an Array of Option.

The problem is with the type of options back at the point where you create it (in prepareForSegue). You don't show that code, but I am betting that you fail to cast / type it at that point. That's why the assignment fails. options may be an array of things that do in fact happen to adopt Option, but that's not enough; it must be typed as an array of Option.

So, back in prepareForSegue, form your options like this:

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

Now you will be able to assign it directly to destinationViewController.options.

Here's a quick test case (in a playground; I detest playgrounds, but they can have their uses):

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

(I also tested this in an actual app with an actual prepareForSegue, and it works fine.)

I was having the same problem and fixed it marking my protocol with @objc, in your case it would look like this

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

Got the solution from this answer

This one also works fine

destinationViewController.options = options.map{$0}