«Erreur fatale: le tableau ne peut pas être ponté depuis Objective-C» - Pourquoi essayez-vous même, Swift?

92

J'ai déclaré un protocole Swift:

protocol Option {
    var name: String { get }
}

Je déclare plusieurs implémentations de ce protocole - certaines classes, certaines énumérations.

J'ai un contrôleur de vue avec une propriété déclarée comme suit:

var options: [Option] = []

Lorsque j'essaie de définir cette propriété sur un tableau d'objets qui implémentent le Optionprotocole dans un autre VC prepareForSegue, j'obtiens une erreur d'exécution:

fatal error: array cannot be bridged from Objective-C

Pourquoi ça ne marche pas? Le compilateur a toutes les informations dont il a besoin, et je ne comprends pas du tout ce que Objective-C a à voir avec lui - mon projet ne contient que des fichiers Swift, et ces tableaux ne viennent ni ne sortent d'aucune méthode de framework qui nécessiter leur pontage NSArray.

Robert Atkins
la source
6
Avez-vous essayé de pré-ajouter @objcvotre protocole? stackoverflow.com/a/28029568/377369
Fabio Poloni
1
Cela ne fonctionne pas si l'une des implémentations de protocole est une énumération: "Le type non-classe 'Foo' ne peut pas se conformer au protocole de classe 'Option'"
Robert Atkins
Pourquoi cela doit-il être un protocole de classe? Je ne le transmets pas à un framework Obj-C ou à toute autre chose qui nécessite que le Swift Array soit ponté vers NSArray.
Robert Atkins
La manière dont Swift et Objective-C travaillent ensemble est toujours un secret pour moi. Je dois juste "accepter" beaucoup de choses qui "marchent" ou "ne marchent pas".
Fabio Poloni
9
Pourquoi celui-ci a-t-il autant de votes négatifs? Cela me semble être une question juste et claire.
Guven du

Réponses:

83

J'ai trouvé une solution. C'est assez ... insatisfaisant , mais ça marche. Où je place le tableau sur le contrôleur de vue de destination, je fais:

destinationViewController.options = options.map({$0 as Option})
Robert Atkins
la source
ne pouvez-vous pas lancer tout le tableau? options as [Option]
Kostiantyn Koval
Nan. Je l'ai essayé (Xcode 6.3.1 (6D1002)), ne fonctionne pas. Je ne devrais pas avoir besoin de le lancer dans tous les cas, le compilateur sait que je passe dans un tableau de choses qui implémentent Option.
Robert Atkins
2
"un tableau de choses qui implémentent l'option" Ah, mais ce n'est pas la même chose qu'un tableau d'options, ce dont vous avez besoin. Voyez ma réponse.
mat
1
Cela fonctionne, et oui, c'est très insatisfaisant ... cela ne devrait pas être nécessaire. Swift devrait être capable de gérer les htis.
Oscar Gomez
Je suis d'accord ... cela fonctionne de cette façon, mais c'est un morceau de code très insatisfaisant
Michael
22

le compilateur sait que je passe dans un tableau de choses qui implémentent Option

Vous avez laissé échapper une remarque très révélatrice, qui suggère la source du problème. Un «tableau de choses qui implémentent l'option» n'est pas un tableau d'options.

Le problème vient du type de optionsdos au point où vous le créez (en prepareForSegue). Vous ne montrez pas ce code, mais je parie que vous ne parvenez pas à le lancer / le taper à ce stade. C'est pourquoi la mission échoue. optionspeut être un éventail de choses qui adoptent effectivement Option, mais ce n'est pas suffisant; il doit être saisi comme un tableau d'Option.

Alors, de retour prepareForSegue, formez votre optionscomme ceci:

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

Maintenant , vous serez en mesure d'attribuer directement à destinationViewController.options.

Voici un cas de test rapide (dans une aire de jeux; je déteste les aires de jeux, mais elles peuvent avoir leurs utilisations):

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

(J'ai également testé cela dans une application réelle avec un réel prepareForSegue, et cela fonctionne bien.)

mat
la source
1
Je pense que cela est rompu à l'extrême parce que le compilateur ne sait à l' exécution de cette chose est une option. Et dans tous les cas, comme indiqué dans le commentaire de ma propre réponse ci-dessous, ni casting ( viewController.options = things as [Option]) ni création d'une variable temporaire explicitement tapée [Option]comme vous le suggérez ici ne fonctionnent réellement. Dans les deux cas, j'obtiens l'erreur d'exécution.
Robert Atkins
Ensuite, vous devez expliquer pourquoi cela fonctionne pour moi. Il se passe autre chose que vous n'avez pas déclaré. Si vous ne révélez pas plus de code, je dois simplement soupçonner que vous retenez quelque chose d'essentiel.
mat
Peut être. Mais je suis toujours confus quant à ce que cela a à voir avec Objective-C en premier lieu (vis. L'erreur d'exécution d'origine.) Je ne fais rien (que je puisse voir) qui devrait forcer un casting de pont à NSArray.
Robert Atkins
2
Vois-le de cette façon. Je vous ai montré du code qui fonctionne. Vous ne m'avez pas montré de code qui ne fonctionne pas - je ne peux pas reproduire votre problème à partir des données fournies. Aidez-moi à le reproduire.
mat
1
@ CristiBăluță C'est ce que vous auriez besoin de savoir avant de prétendre que "ce problème n'est toujours pas résolu"
matt
16

J'avais le même problème et je l'ai corrigé en marquant mon protocole avec @objc, dans votre cas, cela ressemblerait à ceci

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

Vous avez la solution de cette réponse

Juan
la source
1
Comme dans les commentaires sur la question d'origine, cela ne fonctionne pas si l'un des implémenteurs du protocole est Swift Enums. Ce qui dans mon cas c'est le cas.
Robert Atkins
obcj typo should be objc
Alan Scarpa
1

Celui-ci fonctionne également très bien

destinationViewController.options = options.map{$0}
Mykola Denysyuk
la source