“Erro fatal: a matriz não pode ser conectada a Objective-C” - Por que você está tentando, Swift?

92

Eu declarei um protocolo Swift:

protocol Option {
    var name: String { get }
}

Eu declaro várias implementações deste protocolo - algumas classes, alguns enums.

Eu tenho um controlador de visualização com uma propriedade declarada assim:

var options: [Option] = []

Quando tento definir essa propriedade para uma matriz de objetos que implementam o Optionprotocolo em outro VC prepareForSegue, recebo um erro de tempo de execução:

fatal error: array cannot be bridged from Objective-C

Por que isso não funciona? O compilador tem todas as informações de que precisa, e eu não entendo o que Objective-C tem a ver com ele - meu projeto contém apenas arquivos Swift, e esses arrays não estão entrando ou saindo de quaisquer métodos de framework que necessitam que eles sejam ligados NSArray.

Robert Atkins
fonte
6
Você tentou preceder o @objcseu protocolo? stackoverflow.com/a/28029568/377369
Fabio Poloni
1
Isso não funciona se qualquer uma das implementações de protocolo for um enum: "Tipo de não classe 'Foo' não pode estar em conformidade com o protocolo de classe 'Opção'"
Robert Atkins
Por que deve ser um protocolo de classe? Não estou passando para uma estrutura Obj-C ou qualquer outra coisa que exija que o Swift Array seja conectado ao NSArray.
Robert Atkins
A maneira como o Swift e o Objective-C trabalham juntos ainda é um segredo para mim. Eu apenas tenho que "aceitar" muitas coisas que apenas "funcionam" ou "não funcionam".
Fabio Poloni
9
Por que esse aqui tem tantos votos negativos? Parece uma pergunta justa e clara para mim.
Guven

Respostas:

83

Eu encontrei uma solução. É bastante ... insatisfatório , mas funciona. Onde eu defino a matriz no controlador de visualização de destino, eu faço:

destinationViewController.options = options.map({$0 as Option})
Robert Atkins
fonte
você não pode lançar array inteiro? options as [Option]
Kostiantyn Koval
Não. Tentei (Xcode 6.3.1 (6D1002)), não funciona. Eu não deveria precisar lançá-lo em nenhum caso, o compilador sabe que estou transmitindo um Array de coisas que implementam Option.
Robert Atkins
2
"um Array de coisas que implementam Option" Ah, mas isso não é o mesmo que um Array of Option, que é o que você precisa. Veja minha resposta.
sábado
1
Isso funciona, e sim, é muito insatisfatório ... isso não deveria ser necessário. O Swift deve ser capaz de lidar com o htis.
Oscar Gomez de
Concordo ... funciona assim, mas é um código muito insatisfatório
Michael
22

o compilador sabe que estou transmitindo uma série de coisas que implementam Option

Você deixou escapar uma observação muito reveladora, que sugere a origem do problema. Uma "Matriz de coisas que implementam Opção" não é uma Matriz de Opções.

O problema é com o tipo de optionscostas no ponto onde você as cria (dentro prepareForSegue). Você não mostra esse código, mas aposto que você não conseguiu lançar / digitá-lo nesse ponto. É por isso que a atribuição falha. optionspode ser uma série de coisas que de fato acontecem para adotar Option, mas isso não é suficiente; deve ser digitado como uma matriz de Option.

Então, de volta prepareForSegue, forme optionsassim:

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

Agora você poderá atribuí-lo diretamente a destinationViewController.options.

Aqui está um caso de teste rápido (em um playground; eu detesto playgrounds, mas eles podem ter seu uso):

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

(Eu também testei isso em um aplicativo real com um real prepareForSeguee funciona bem.)

mate
fonte
1
Eu acho que isso é quebrado no extremo porque o compilador não sabe em tempo de execução que coisa é uma opção. E, em qualquer caso, conforme observado no comentário à minha própria resposta abaixo, nem casting ( viewController.options = things as [Option]) nem criar uma variável temporária explicitamente digitada [Option]como você sugere aqui realmente funciona. Em ambos os casos, recebo o erro de tempo de execução.
Robert Atkins
Então você tem que explicar por que funciona para mim. Algo mais está acontecendo que você não declarou. Se você não revelar mais código, simplesmente suspeito que você está escondendo algo essencial.
sábado
Talvez. Mas ainda estou confuso sobre o que isso tem a ver com Objective-C em primeiro lugar (vis. O erro de tempo de execução original). Não estou fazendo nada (que eu posso ver) que deva forçar um elenco de ponte para NSArray.
Robert Atkins
2
Olhe isto deste modo. Eu mostrei um código que funciona. Você não me mostrou um código que não funciona - não posso reproduzir seu problema a partir dos dados fornecidos. Ajude-me a reproduzi-lo.
sábado
1
@ CristiBăluță Isso é o que você precisa descobrir antes de alegar "este problema ainda não foi corrigido"
matt
16

Eu estava com o mesmo problema e resolvi marcando meu protocolo com @objc, no seu caso ficaria assim

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

Consegui a solução com esta resposta

Juan
fonte
1
Como nos comentários sobre a questão original, isso não funciona se algum dos implementadores do protocolo for Swift Enums. Que no meu caso eles são.
Robert Atkins de
typo obcj deve ser objc
Alan Scarpa
1

Este também funciona bem

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