"Error fatal: la matriz no se puede puentear desde Objective-C". ¿Por qué lo intentas, Swift?

92

He declarado un protocolo Swift:

protocol Option {
    var name: String { get }
}

Declaro múltiples implementaciones de este protocolo: algunas clases, algunas enumeraciones.

Tengo un controlador de vista con una propiedad declarada así:

var options: [Option] = []

Cuando intento establecer esta propiedad en una matriz de objetos que implementan el Optionprotocolo en otro VC prepareForSegue, obtengo un error de tiempo de ejecución:

fatal error: array cannot be bridged from Objective-C

¿Por qué no funciona esto? El compilador tiene toda la información que necesita y no entiendo en absoluto qué tiene que ver Objective-C con él: mi proyecto solo contiene archivos Swift, y estas matrices no entran ni salen de ningún método marco que necesitan ser puenteados NSArray.

Robert Atkins
fuente
6
¿Intentaste anteponer @objctu protocolo? stackoverflow.com/a/28029568/377369
Fabio Poloni
1
Eso no funciona si alguna de las implementaciones del protocolo es una enumeración: "El tipo que no es de clase 'Foo' no puede ajustarse al protocolo de clase 'Opción'"
Robert Atkins
Sin embargo, ¿por qué debe ser un protocolo de clase? No lo voy a pasar a un marco Obj-C ni a ninguna otra cosa que requiera que Swift Array se conecte a NSArray.
Robert Atkins
La forma en que Swift y Objective-C trabajan juntos sigue siendo un secreto para mí. Solo tengo que "aceptar" muchas cosas que simplemente "funcionan" o "no funcionan".
Fabio Poloni
9
¿Por qué este tiene tantos votos negativos? Me parece una pregunta clara y justa.
Guven

Respuestas:

83

He encontrado una solución. Es bastante ... insatisfactorio , pero funciona. Donde configuro la matriz en el controlador de vista de destino, hago:

destinationViewController.options = options.map({$0 as Option})
Robert Atkins
fuente
¿No puedes lanzar toda la matriz? options as [Option]
Kostiantyn Koval
No Lo intenté (Xcode 6.3.1 (6D1002)), no funciona. No debería necesitar lanzarlo en ningún caso, el compilador sabe que estoy pasando una matriz de cosas que implementan Option.
Robert Atkins
2
"una matriz de cosas que implementan la opción" Ah, pero eso no es lo mismo que una matriz de opciones, que es lo que necesita. Mira mi respuesta.
Matt
1
Esto funciona, y sí, es muy insatisfactorio ... esto no debería ser necesario. Swift debería poder manejar esto.
Oscar Gomez
Estoy de acuerdo ... funciona de esta manera, pero es un código muy insatisfactorio
Michael
22

el compilador sabe que estoy pasando una matriz de cosas que implementan Option

Dejó escapar un comentario muy revelador, que sugiere la fuente del problema. Una "Matriz de cosas que implementan Option" no es una Matriz de Opciones.

El problema es con el tipo de optionsespalda en el punto donde lo creaste (adentro prepareForSegue). No muestra ese código, pero apuesto a que no puede lanzarlo / escribirlo en ese momento. Por eso falla la tarea. optionspuede haber una serie de cosas que de hecho suceden para adoptar Option, pero eso no es suficiente; debe escribirse como una matriz de Option.

Entonces, de vuelta prepareForSegue, forme su optionsasí:

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

Ahora podrás asignarlo directamente a destinationViewController.options.

Aquí hay un caso de prueba rápido (en un patio de recreo; detesto los patios de recreo, pero pueden tener sus usos):

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

(También probé esto en una aplicación real con una real prepareForSegue, y funciona bien).

mate
fuente
1
Creo que esto se rompe en el extremo porque el compilador no sabe en tiempo de ejecución que cosa es una opción. Y, en cualquier caso, como se indica en el comentario de mi propia respuesta a continuación, ni la conversión ( viewController.options = things as [Option]) ni la creación de una variable temporal escrita explícitamente [Option]como sugiere aquí, en realidad funciona. En ambos casos aparece el error de tiempo de ejecución.
Robert Atkins
Entonces tienes que explicar por qué funciona para mí. Algo más está sucediendo que no ha dicho. Si no revela más código, simplemente tengo que sospechar que está reteniendo algo esencial.
Matt
Tal vez. Pero todavía estoy confundido en cuanto a qué tiene que ver esto con Objective-C en primer lugar (vis. El error de tiempo de ejecución original). No estoy haciendo nada (que pueda ver) que debería forzar un lanzamiento de puente a NSArray.
Robert Atkins
2
Míralo de esta manera. Te he mostrado un código que funciona. No me ha mostrado un código que no funciona; no puedo reproducir su problema a partir de los datos proporcionados. Ayúdame a reproducirlo.
Matt
1
@ CristiBăluță Eso es lo que debe averiguar antes de afirmar que "este problema aún no se ha solucionado"
Matt
16

Estaba teniendo el mismo problema y lo arreglé marcando mi protocolo con @objc, en tu caso se vería así

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

Obtuve la solución de esta respuesta

Juan
fuente
1
Como en los comentarios sobre la pregunta original, esto no funciona si alguno de los implementadores del protocolo es Swift Enums. Que en mi caso son.
Robert Atkins
typo obcj debería ser objc
Alan Scarpa
1

Este también funciona bien

destinationViewController.options = options.map{$0}
Mykola Denysyuk
fuente