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?
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.
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 nil
s 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
}
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.