J'ai commencé à utiliser swiftLint et j'ai remarqué que l'une des meilleures pratiques pour Swift consiste à éviter le déploiement forcé. Cependant, je l'ai beaucoup utilisé lors de la manipulation de tableView, collectionView pour les cellules:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellID, forIndexPath: indexPath) as! MyOffersViewCell
Si ce n'est pas la meilleure pratique, quelle est la bonne façon de gérer cela? Je suppose que je peux utiliser si let with as? Est-ce acceptable?
if let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellID, forIndexPath: indexPath) as? MyOffersViewCell {
// code
} else {
// code
}
Cette question est probablement basée sur l'opinion, alors prenez ma réponse avec un grain de sel, mais je ne dirais pas que baisser la force est toujours mauvais; il vous suffit de considérer la sémantique et son application dans une situation donnée.
as! SomeClass
est un contrat, il dit essentiellement "Je garantis que cette chose est une instance de SomeClass". S'il s'avère que ce n'est pas SomeClass, une exception sera levée car vous avez violé le contrat.
Vous devez tenir compte du contexte dans lequel vous utilisez ce contrat et des mesures appropriées à prendre si vous n'utilisiez pas la force baissée.
Dans l'exemple que vous donnez, si dequeueReusableCellWithIdentifier
ne} _ vous donne une MyOffersViewCell
, alors vous avez probablement mal configuré quelque chose à faire avec l'identificateur de réutilisation de cellule et une exception vous aidera à résoudre ce problème.
Si vous avez utilisé un downcast conditionnel, vous n'obtiendrez plus rien et devrez gérer cela d'une manière ou d'une autre - Enregistrez-vous un message? Lancer une exception? Cela représente certainement une erreur irrécupérable et quelque chose que vous voulez trouver pendant le développement; vous ne vous attendriez pas à devoir gérer cela après la publication. Votre code ne va pas soudainement commencer à renvoyer différents types de cellules. Si vous laissez simplement le code planter sur la force décroissante, il indiquera directement la ligne où le problème s'est produit.
A présent, considérons le cas où vous accédez à certains JSON extraits d'un service Web. Il se peut que le service Web change en dehors de votre contrôle. Par conséquent, Nice pourrait vous aider à le gérer avec plus de grâce. Votre application peut ne pas fonctionner, mais vous pouvez au moins afficher une alerte plutôt que de vous bloquer.
BAD - Se bloque si JSON n'est pas un tableau
let someArray=myJSON as! NSArray
...
Mieux - Gestion des JSON invalides avec une alerte
guard let someArray=myJSON as? NSArray else {
// Display a UIAlertController telling the user to check for an updated app..
return
}
Mettre à jour
Après avoir utilisé Swiftlint pendant un moment, je suis maintenant totalement converti au culte Zero Force-Unwrapping (conformément au commentaire de @ Kevin ci-dessous).
Il n'y a pas vraiment de situation dans laquelle vous devez forcer de dérouler une option pour laquelle vous ne pouvez pas utiliser plutôt if let...
, guard let... else
ou switch... case let...
.
Donc, maintenant je ferais ceci:
for media in mediaArray {
if let song = media as? Song {
// use Song class's methods and properties on song...
} else if let movie = media as? Movie {
// use Movie class's methods and properties on movie...
}
}
... ou si vous préférez l'élégance et la sécurité d'un énoncé switch
exhaustif par rapport à une chaîne de if/else
s sujette aux bogues, alors:
switch media {
case let song as Song:
// use Song class's methods and properties on song...
case let movie as Movie:
// use Movie class's methods and properties on movie...
default:
// Deal with any other type as you see fit...
}
... ou mieux, utilisez flatMap()
pour transformer mediaArray
en deux tableaux (éventuellement vides) typés de types [Song]
et [Movie]
respectivement. Mais cela n'entre pas dans le cadre de la question (force-wrapper) ...
De plus, je ne forcerai pas le déroulement même lors de la mise en file d'attente des cellules de la vue tableau. Si la cellule retirée de la file d'attente ne peut pas être convertie dans la sous-classe UITableViewCell
appropriée, cela signifie qu'il y a un problème avec mes story-boards. caution avec fatalError()
.
Réponse originale (pour l'enregistrement)
En plus de la réponse de Paulw11, ce modèle est complètement valide, sûr et utile parfois:
if myObject is String {
let myString = myObject as! String
}
Prenons l'exemple donné par Apple: un tableau d'instances Media
, pouvant contenir des objets Song
ou Movie
(les deux sous-classes de Media):
let mediaArray = [Media]()
// (populate...)
for media in mediaArray {
if media is Song {
let song = media as! Song
// use Song class's methods and properties on song...
}
else if media is Movie {
let movie = media as! Movie
// use Movie class's methods and properties on movie...
}
"Force Cast" a sa place, lorsque vous savez que ce que vous projetez est de ce type, par exemple.
Supposons que nous savons que myView
a une sous-vue qui est une UILabel
avec le tag 1
, nous pouvons aller de l'avant et forcer la conversion de UIView
à UILabel
safety:
myLabel = myView.viewWithTag(1) as! UILabel
Sinon, l'option la plus sûre consiste à utiliser une protection.
guard let myLabel = myView.viewWithTag(1) as? UILabel else {
... //ABORT MISSION
}
Ce dernier est plus sûr car il traite évidemment tous les cas graves, mais le premier est plus facile. Donc, il s’agit vraiment de préférence personnelle, de savoir si l’on peut changer quelque chose à l’avenir ou si on ne sait pas si ce que vous allez dévoiler sera ce que vous voulez utiliser comme solution de rechange. Dans cette situation, un garde sera toujours le bon choix.
Pour résumer: si vous savez exactement ce que ce sera, vous pouvez forcer le lancement, sinon, au moindre risque, il pourrait être autre chose d'utiliser un garde
D'autres ont écrit sur un cas plus général, mais je veux donner ma solution à ce cas précis:
guard let cell = tableView.dequeueReusableCell(
withIdentifier: PropertyTableViewCell.reuseIdentifier,
for: indexPath) as? PropertyTableViewCell
else {
fatalError("DequeueReusableCell failed while casting")
}
En gros, enroulez-le autour d'une instruction guard
et convertissez-le éventuellement avec as?
.
Lorsque vous travaillez avec vos types et que vous êtes sûr qu'ils ont le type attendu et qu'ils ont toujours des valeurs, la conversion doit être forcée. Si vos applications se bloquent, vous pouvez facilement savoir que vous avez une erreur concernant une partie de l'interface utilisateur, de la cellule Dequeuing, ...
Mais quand vous allez lancer des types que vous ne connaissez pas, est-ce toujours le même type? Ou est-ce toujours avoir de la valeur? Vous devriez éviter de dérouler de force
Comme JSON provenant d'un serveur dont vous ne savez pas quel type est celui-ci ou dont l'une des clés a une valeur ou non
Désolé pour mon mauvais anglais, j'essaie de m'améliorer
Bonne chance????????