Como faço para armazenar um valor do tipo Classe em um dictionary do tipo no Swift?

Eu quero armazenar um tipo mais especializado em um dictionary do tipo [String: SomeClass]. Aqui está um exemplo de código ilustrando o meu problema (também disponível para jogar em https://swiftlang.ng.bluemix.net/#/repl/579756cf9966ba6275fc794a ):

class Thing {} protocol Flavor {} class Vanilla: Flavor {} var dict = [String:Thing]() dict["foo"] = Thing() 

Ele produz o erro ERROR at line 9, col 28: cannot assign value of type 'Thing' to type 'Thing?' .

Eu tentei lançar Thing() as Thing mas isso produz o erro cannot convert value of type 'Thing' to type 'Thing' in coercion .

Eu também tentei definir o dictionary como tipo [String:Thing] mas isso também não muda nada.

Como faço para criar uma coleção de coisas diferentes sem recorrer a simples [String:AnyObject] ?

Eu também devo mencionar que a class Thing não é definida por mim (na verdade é sobre o BoltsSwift Task s), então a solução para criar uma class base de Thing sem um parâmetro de tipo não funciona.

Uma Thing não é uma Thing . Thing não é covariante. Não há como Swift expressar que Thing é covariante. Há boas razões para isto. Se o que você estava pedindo fosse permitido sem regras cuidadosas, eu poderia escrever o seguinte código:

 func addElement(array: inout [Any], object: Any) { array.append(object) } var intArray: [Int] = [1] addElement(array: &intArray, object: "Stuff") 

Int é um subtipo de Any , portanto, se [Int] fosse um subtipo de [Any] , eu poderia usar essa function para append strings a um array int. Isso quebra o sistema de tipos. Não faça isso.

Dependendo da sua situação exata, existem duas soluções. Se for um tipo de valor, reembale-o:

 let thing = Thing(value: Vanilla()) dict["foo"] = Thing(value: thing.value) 

Se for um tipo de referência, coloque-o com um tipo de borracha . Por exemplo:

 // struct unless you have to make this a class to fit into the system, // but then it may be a bit more complicated struct AnyThing { let _value: () -> Flavor var value: Flavor { return _value() } init(thing: Thing) { _value = { return thing.value } } } var dict = [String:AnyThing]() dict["foo"] = AnyThing(thing: Thing(value: Vanilla())) 

As especificidades do tipo borracha podem ser diferentes dependendo do seu tipo subjacente.


BTW: Os diagnósticos em torno disso ficaram muito bons. Se você tentar chamar meu addElement acima no Xcode 9, você addElement isto:

 Cannot pass immutable value as inout argument: implicit conversion from '[Int]' to '[Any]' requires a temporary 

O que isto está lhe dizendo é que Swift está disposto a passar [Int] onde você pede [Any] como um caso especial para Arrays (embora este tratamento especial não seja estendido para outros tipos genéricos). Mas só permitirá isso fazendo uma cópia temporária (imutável) da matriz. (Esse é outro exemplo em que pode ser difícil raciocinar sobre o desempenho do Swift. Em situações que parecem “transmitir” em outros idiomas, o Swift pode fazer uma cópia. Ou talvez não. É difícil ter certeza.)

Uma maneira de resolver isso é adicionar um inicializador ao Thing e criar um Thing que conterá um object Vanilla .

Será algo parecido com:

 class Thing { init(thing : T) { } } protocol Flavor {} class Vanilla: Flavor {} var dict = [String:Thing]() dict["foo"] = Thing(thing: Vanilla())