web-dev-qa-db-fra.com

Traiter un downcast forcé comme facultatif ne produira jamais "nul"

Je me suis amusé avec Swift et j'ai découvert que lorsque je rédige un objet à insérer dans un dictionnaire, je reçois un avertissement étrange: Treating a forced downcast to 'String' as optional will never produce 'nil'. Si je remplace as par as?, l'avertissement disparaîtra.

func test() -> AnyObject! {
  return "Hi!"
}

var dict = Dictionary<String,String>()
dict["test"]=test() as String

La documentation d'Apple dit ce qui suit

Étant donné que le downcasting peut échouer, l'opérateur de conversion de type se présente sous deux formes différentes. Le formulaire facultatif, as?, renvoie une valeur facultative du type que vous essayez de convertir. La forme forcée, as, tente l'abaissement et force le résultat en une seule action composée.

Je ne vois pas trop pourquoi utiliser as? au lieu de as est correct ici. Certains tests révèlent que si je modifie test () pour renvoyer un Int au lieu d'une String, le code se ferme avec une erreur si je continue à utiliser as. Si je passe à l'aide de as?, le code continuera son exécution normalement et ignorera cette instruction (dict restera vide). Cependant, je ne sais pas pourquoi cela est préférable. À mon avis, je préférerais que le programme quitte avec une erreur et me signale que la distribution a échoué, puis ignore simplement la déclaration erronée et continue à exécuter.

Selon la documentation, je devrais utiliser la forme forcée "uniquement lorsque vous êtes sûr que le downcast réussira toujours". Dans ce cas, je suis sûr que le downcast réussira toujours puisque je sais que test() ne peut que renvoyer une chaîne, alors je suppose que c'est une situation idéale pour la forme forcée de down casting. Alors pourquoi le compilateur me donne-t-il un avertissement?

29
Pamelloes

Examinons de plus près votre dernière ligne et décomposez-la pour voir ce qui se passe:

let temporaryAnyObject = test()
let temporaryString = temporaryAnyObject as String
dict["test"] = temporaryString

L'erreur est sur la deuxième ligne, où vous demandez au compilateur d'appliquer que temporaryAnyObject est définitivement un String. Le code sera toujours compilé (si vous ne considérez pas les avertissements comme des erreurs), mais se bloquera si temporaryAnyObject n'est pas réellement un String

La façon dont fonctionne as, sans ?, dit essentiellement "à partir de maintenant, traitez le résultat de cette expression comme ce type SI le résultat est en fait de ce type, sinon nous sommes devenus incohérents et ne pouvons plus être exécutés.

La façon dont fonctionne as? (avec le ?) dit "à partir de maintenant, traitez le résultat de cette expression comme ce type SI le résultat est en fait de ce type, sinon le résultat de cette expression est nil.

Ainsi, dans mon exemple éclaté ci-dessus, si test() renvoie une String, la as downcast réussit et temporaryString est maintenant une String. Si test() ne renvoie pas une String, mais plutôt une Int ou tout autre élément non sous-classé dans String, la as échoue et le code ne peut plus continuer à être exécuté. 

En effet, en tant que développeur ayant le contrôle total, vous avez dit au système de se comporter de cette manière en ne mettant pas l'indicateur facultatif ?. La commande as signifie spécifiquement que vous ne tolérez pas le comportement facultatif et que vous avez besoin que cette opération de downcast fonctionne.

Si vous aviez mis le ?, alors temporaryString serait nil et la troisième ligne supprimerait simplement la paire clé/valeur "test" du dictionnaire.

Cela peut sembler étrange, mais ce n’est que parce que c’est le comportement par défaut opposé de nombreuses langues, comme Obj-C, qui considère tout comme optionnel par défaut et vous permet de placer vos propres vérifications et affirmations.

Modifier - Mise à jour Swift 2

Depuis Swift 2, l'opérateur de downcast forcé et disponible, as, a été supprimé et est remplacé par as!, qui est beaucoup plus rapide. Le comportement est le même.

43
Ryan

Vous pouvez résoudre cet avertissement sous deux angles. 1. La valeur que vous renvoyez 2. Le type que vous êtes attendu à renvoyer. L'autre réponse parle du premier angle. Je parle du 2ème angle

Cela est dû au fait que vous retournez une valeur forcée non emballée et convertie pour un facultatif. Le compilateur est comme "Si vous voulez vraiment forcer la conversion de tous les optionnels, alors pourquoi ne pas simplement faire en sorte que le paramètre de retour attendu soit un non-optionnel"

Par exemple si vous avez écrit

func returnSomething<T> -> T?{ // I'm an optional, I can handle nils SAFELY and won't crash.

return UIViewController as! T // will never return a safe nil, will just CRASH

}

En gros, vous vous êtes dit (et au compilateur) que je veux manipuler nils en toute sécurité, mais à la ligne suivante, vous avez dit non, je ne le fais pas !!!

Le compilateur donnerait un avertissement:

Traiter un downcast forcé sur "T" comme une option ne produira jamais "nil"

Une alternative consiste à supprimer le ?

func returnSomething<T>() -> T{ // I can't handle nils

    return UIViewController() as! T // I will crash on nils
}

Cela dit, le meilleur moyen est probablement de ne pas utiliser la force et de faire:

func returnSomething<T>() -> T?{ // I can handle nils

    return UIViewController() as? T // I won't crash on nils
}
3
Honey

Il semble y avoir un bogue à propos de cet avertissement il y a quelques années maintenant ... https://bugs.Swift.org/browse/SR-4209 Cela montre donc même dans des situations où il est évident que vous faites et à droite. 

0
Renetik