web-dev-qa-db-fra.com

Swift: transtypage Any to array of protocol objects

Il y a un protocole:

protocol Valuable {
    func value() -> Int
}

et une classe qui implémente le protocole:

class Value: Valuable {
    private let v: Int

    init(value: Int) {
        v = value
    }

    func value() -> Int {
        return v
    }
}

Il existe un tableau d'objets Value stockés dans une variable de type Any:

let any: Any = [Value(value: 1), Value(value: 2), Value(value: 3)]

Il est possible de convertir n'importe quel élément en [Valeur]:

let arrayOfValue = any as? [Value] // [1, 2, 3]

Pourquoi n'est-il pas possible de valider Any to [Valuable]?

let arrayOfValuable = any as! [Valuable] // compiler error BAD INSTRUCTION
let arrayOfValuable2 = any as? [Valuable] // nil
15
Andrii H.

Mise à jour: Dans Swift3, il est tout à fait possible de convertir [Any] en [Valuable]. La distribution réussira tant que tous les éléments du tableau pourront être convertis; la distribution échouera sinon.

var strings: [Any] = ["cadena"]
var mixed: [Any] = ["cadena", 12]

strings as! [String] // ["cadena"]
mixed as? [String] // nil
mixed as! [String] // Error! Could not cast value...

Auparavant, à compter de Swift 2: Pour créer un [Valuable] à partir d'un [Any], vous devez le faire manuellement avec des fonctions telles que map, comme d'autres réponses l'ont expliqué.

Il n'y a actuellement pas de covariance ni contravariance avec des génériques dans Swift (à compter de Swift 2). Cela signifie que des tableaux de types différents, tels que [String] ou [UIView], ne peuvent pas être convertis les uns dans les autres, ni leurs types comparés.

[UIView] et [UIButton] n'ont aucune hiérarchie entre eux, même si UIButton est une sous-classe de UIView. C'est pourquoi même si ce qui suit retourne true:

Valuable.self is Any.Type // true

les distributions suivantes génèrent des erreurs pour la même raison: 

var anyArray: [Any] = ["cadena"]

anyArray as! [String] // BAD_INSTRUCTION error
"some string" as! Double // BAD_INSTRUCTION error

Les deux classes ne portent aucune relation et la distribution est impossible, car le as! est une distribution forcée, ce qui provoque une erreur.

6
LopSae

Je fais un peu de Dig et vous devez ajouter l'attribut @objc comme suit 

@objc
protocol Valuable {
    func value() -> Int
}

class Value: Valuable {
    private let v: Int

    init(value: Int) {
        v = value
    }

    @objc func value() -> Int {
        return v
    }
}

let any: AnyObject = [Value(value: 1), Value(value: 2), Value(value: 3)]

let arrayOfValueable = any as! [Valuable] // [{v 1}, {v 2}, {v 3}]

Pour plus d'informations et pour obtenir une réponse à "pourquoi?": https://stackoverflow.com/a/25589323/989631

J'espère que ceci vous aidera.

Modifier

De plus, cela ne fonctionne que si vous utilisez AnyObject au lieu de Any :(

3
Błażej

Ça marche pour moi:

let arrayOfValuable = arrayOfValue?.map { $0 as Valuable }

ou le même:

let arrayOfValuable2 = (any as? [Value])?.map { $0 as Valuable }

En conclusion, arrayOfValuable devrait avoir le type de [Valuable]?

Modifier:

Ou essayez ceci:

let arrayOfAny: [Any] = [Value(value: 1), Value(value: 2), Value(value: 3)]
let arrayOfValuable3 = arrayOfAny.map { $0 as Valuable }

Bien sûr, le meilleur moyen de le faire est de déclarer arrayOfAny en tant que [Valuable], afin que vous n'ayez plus de problèmes par la suite.

2
Beraliv

Casting Array [Any] à Array [String]

let arrayOfAny:Array<Any> = [1,"2",3.0,CGFloat(4)]
let stringArray:Array<String> = arrayOfAny.map {String($0)}
print(stringArray)//"1", "2", "3.0","4.0"

Conclusion:
Parfois, il est utile de convertir un type de tableau en un autre. La meilleure approche consiste généralement à ne pas convertir le type de tableau, mais à effectuer une vérification d'instance avant de compresser le tableau ou une vérification d'instance après le décompression du tableau

0
eonist