¿Cómo almaceno un valor de tipo Class en un Dictionary de tipo en Swift?

Quiero almacenar un tipo más especializado en un Diccionario de tipo [String: SomeClass]. Aquí hay un código de muestra que ilustra mi problema (también está disponible para jugar en https://swiftlang.ng.bluemix.net/#/repl/579756cf9966ba6275fc794a ):

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

Produce el error ERROR at line 9, col 28: cannot assign value of type 'Thing' to type 'Thing?' .

He intentado cannot convert value of type 'Thing' to type 'Thing' in coercion Thing() as Thing pero eso produce el error. cannot convert value of type 'Thing' to type 'Thing' in coercion .

También traté de definir el diccionario como tipo [String:Thing] pero eso tampoco cambia nada.

¿Cómo creo una colección de cosas diferentes sin recurrir a [String:AnyObject] ?

También debo mencionar que la Thing clase no está definida por mí (de hecho, se trata de Task BoltsSwift), por lo que la solución para crear una clase base de Thing sin un parámetro de tipo no funciona.

Una Thing no es una Thing . Thing no es covariante. No hay forma en Swift de express que Thing es covariante. Hay buenas razones para esto. Si lo que estaba pidiendo estuviera permitido sin reglas cuidadosas, podría escribir el siguiente código:

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

Int es un subtipo de Any , así que si [Int] fuera un subtipo de [Any] , podría usar esta función para anexar cadenas a una matriz int. Eso rompe el sistema de tipos. No hagas eso.

Dependiendo de su situación exacta, hay dos soluciones. Si es un tipo de valor, vuelva a empaquetarlo:

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

Si es un tipo de referencia, márquelo con un tipo de borrador . Por ejemplo:

 // 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())) 

Los detalles del tipo borrador pueden ser diferentes dependiendo de su tipo subyacente.


Por cierto: los diagnósticos alrededor de esto se han vuelto bastante buenos. Si intentas llamar a mi addElement arriba en Xcode 9, obtienes esto:

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

Lo que esto le dice es que Swift está dispuesto a aprobar [Int] donde solicita [Any] como caso especial para Arrays (aunque este tratamiento especial no se extiende a otros tipos generics). Pero solo lo permitirá al hacer una copia temporal (inmutable) de la matriz. (Este es otro ejemplo en el que puede ser difícil razonar sobre el rendimiento de Swift. En situaciones que parecen “lanzar” en otros idiomas, Swift podría hacer una copia. O podría no serlo. Es difícil estar seguro).

Una forma de resolver esto es agregar un inicializador a Thing y crear un Thing que contendrá un objeto Vanilla .

Se verá algo así como:

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