Avant de pouvoir décrire les cas d'utilisation des options implicitement non enveloppées, vous devez déjà comprendre ce que sont les options et options optionnelles implicitement non enveloppées dans Swift. Si vous ne le faites pas, je vous recommande d'abord de lire mon article sur les options
Quand utiliser une option implicitement déballée
Il y a deux raisons principales pour lesquelles on créerait une option implicitement non emballée. Tout cela a à voir avec la définition d'une variable qui ne sera jamais accessible quand nil
sinon, le compilateur Swift vous forcera toujours à déballer explicitement un Facultatif.
1. Une constante qui ne peut pas être définie lors de l'initialisation
Chaque constante de membre doit avoir une valeur au moment où l'initialisation est terminée. Parfois, une constante ne peut pas être initialisée avec sa valeur correcte lors de l'initialisation, mais il peut toujours être garanti d'avoir une valeur avant d'être accessible.
L'utilisation d'une variable facultative permet de contourner ce problème car un facultatif est automatiquement initialisé avec nil
et la valeur qu'il contiendra éventuellement restera immuable. Cependant, il peut être difficile de déballer constamment une variable dont vous savez qu'elle n'est pas nulle. Les options implicitement non enveloppées offrent les mêmes avantages qu'une option, avec l'avantage supplémentaire de ne pas avoir à la déballer explicitement partout.
Un bon exemple de cela est lorsqu'une variable membre ne peut pas être initialisée dans une sous-classe UIView jusqu'à ce que la vue soit chargée:
class MyView: UIView {
@IBOutlet var button: UIButton!
var buttonOriginalWidth: CGFloat!
override func awakeFromNib() {
self.buttonOriginalWidth = self.button.frame.size.width
}
}
Ici, vous ne pouvez pas calculer la largeur d'origine du bouton avant le chargement de la vue, mais vous savez que awakeFromNib
sera appelée avant toute autre méthode sur la vue (autre que l'initialisation). Au lieu de forcer la valeur à être explicitement dépliée de façon inutile dans toute votre classe, vous pouvez la déclarer comme facultative implicitement non enveloppée.
2. Lorsque votre application ne peut pas récupérer d'un être variable nil
Cela devrait être extrêmement rare, mais si votre application ne peut pas continuer à s'exécuter si une variable est nil
accessible, ce serait une perte de temps de s'embêter à la tester nil
. Normalement, si vous avez une condition qui doit absolument être vraie pour que votre application continue de fonctionner, vous utiliserez un assert
. Une option implicitement non enveloppée contient une assertion pour nil. Même alors, il est souvent bon de déballer l'option et d'utiliser une assertion plus descriptive si elle est nulle.
Quand ne pas utiliser une option implicitement déballée
1. Variables des membres calculées avec paresse
Parfois, vous avez une variable membre qui ne doit jamais être nulle, mais elle ne peut pas être définie sur la valeur correcte lors de l'initialisation. Une solution consiste à utiliser une option implicitement non enveloppée, mais une meilleure façon consiste à utiliser une variable paresseuse:
class FileSystemItem {
}
class Directory : FileSystemItem {
lazy var contents : [FileSystemItem] = {
var loadedContents = [FileSystemItem]()
// load contents and append to loadedContents
return loadedContents
}()
}
Désormais, la variable membre contents
n'est initialisée que lors du premier accès. Cela donne à la classe une chance d'entrer dans le bon état avant de calculer la valeur initiale.
Remarque: Cela peut sembler contredire # 1 d'en haut. Cependant, il y a une distinction importante à faire. Ce qui buttonOriginalWidth
précède doit être défini pendant viewDidLoad pour empêcher quiconque de modifier la largeur des boutons avant d'accéder à la propriété.
2. Partout ailleurs
Pour la plupart, les options implicitement non enveloppées doivent être évitées car si elles sont utilisées par erreur, l'intégralité de votre application se bloque lors de son accès nil
. Si vous n'êtes jamais sûr de savoir si une variable peut être nulle, utilisez toujours par défaut une optionnelle normale. Déballer une variable qui n'est jamais nil
certainement ne fait pas grand mal.
if someOptional
.hasValue
est défini directement sur Facultatif. Je préfère la sémantique dehasValue
celle de!= nil
. Je pense que c'est beaucoup plus compréhensible pour les nouveaux programmeurs qui n'ont pas utilisénil
dans d'autres langues.hasValue
est beaucoup plus logique quenil
.hasValue
été retiré de la bêta 6. Ash l'a bien remis ... github.com/AshFurrow/hasValueLes options optionnelles implicitement non encapsulées sont utiles pour présenter une propriété comme non optionnelle lorsqu'elle doit vraiment être optionnelle sous les couvertures. Cela est souvent nécessaire pour «faire le nœud» entre deux objets liés qui ont chacun besoin d'une référence à l'autre. Cela a du sens lorsqu'aucune référence n'est réellement facultative, mais l'un d'eux doit être nul pendant l'initialisation de la paire.
Par exemple:
Toute
B
instance doit toujours avoir unemyBuddyA
référence valide , donc nous ne voulons pas que l'utilisateur la traite comme facultative, mais nous avons besoin qu'elle soit facultative pour que nous puissions construire unB
avant d'avoir unA
se référer.TOUTEFOIS! Ce type d'exigence de référence mutuelle est souvent une indication d'un couplage serré et d'une mauvaise conception. Si vous vous reposez sur des options implicitement déballées, vous devriez probablement envisager une refactorisation pour éliminer les dépendances croisées.
la source
@IBOutlet
weak
déclaration et de supprimer "sans créer un cycle de rétention solide"Les options implicitement déballées sont un compromis pragmatique pour rendre le travail dans un environnement hybride qui doit interagir avec les cadres Cocoa existants et leurs conventions plus agréables, tout en permettant également une migration progressive vers un paradigme de programmation plus sûr - sans pointeurs nuls - imposé par le compilateur Swift.
Swift book, dans le chapitre The Basics , section Implicitly Unwrapped Optionals dit:
Cela revient à utiliser des cas où la non-
nil
gravité des propriétés est établie via la convention d'utilisation et ne peut pas être appliquée par le compilateur lors de l'initialisation de la classe. Par exemple, lesUIViewController
propriétés qui sont initialisées à partir de NIB ou de storyboards, où l'initialisation est divisée en phases distinctes, mais après,viewDidLoad()
vous pouvez supposer que les propriétés existent généralement. Sinon, pour satisfaire le compilateur, vous deviez utiliser le déballage forcé , la liaison facultative ou le chaînage facultatif uniquement pour masquer l'objectif principal du code.La partie ci-dessus du livre Swift se réfère également au chapitre Comptage de référence automatique :
Cela revient aux bizarreries de ne pas être un langage récupéré, donc la rupture des cycles de conservation est sur vous en tant que programmeur et les options implicitement déballées sont un outil pour masquer cette bizarrerie.
Cela couvre le "Quand utiliser les options implicitement déballées dans votre code?" question. En tant que développeur d'applications, vous les rencontrerez principalement dans les signatures de méthodes des bibliothèques écrites en Objective-C, qui n'ont pas la capacité d'exprimer des types facultatifs.
Dans Utilisation de Swift avec Cocoa et Objective-C, section Travailler avec nil :
... et au-delà
la source
Les exemples simples sur une ou plusieurs lignes ne couvrent pas très bien le comportement des options - oui, si vous déclarez une variable et lui fournissez une valeur tout de suite, il n'y a aucun intérêt à une option.
Le meilleur cas que j'ai vu jusqu'à présent est une configuration qui se produit après l'initialisation de l'objet, suivie d'une utilisation "garantie" pour suivre cette configuration, par exemple dans un contrôleur de vue:
Nous savons que
printSize
sera appelé après le chargement de la vue - c'est une méthode d'action connectée à un contrôle à l'intérieur de cette vue, et nous nous sommes assurés de ne pas l'appeler autrement. Nous pouvons donc nous épargner une vérification / liaison facultative avec le!
. Swift ne peut pas reconnaître cette garantie (au moins jusqu'à ce qu'Apple résolve le problème d'arrêt), vous dites donc au compilateur qu'elle existe.Cela rompt cependant la sécurité de type dans une certaine mesure. Partout où vous avez une option implicitement déballée est un endroit où votre application peut planter si votre «garantie» ne tient pas toujours, c'est donc une fonctionnalité à utiliser avec parcimonie. De plus, utiliser
!
tout le temps donne l'impression que vous criez, et personne n'aime ça.la source
CGSizeZero
peut être une bonne valeur sentinelle dans une utilisation réelle. Mais que se passe-t-il si vous avez une taille chargée à partir de nib qui pourrait en fait être nulle? Ensuite, l'utilisation enCGSizeZero
tant que sentinelle ne vous aide pas à faire la distinction entre une valeur non définie et définie sur zéro. De plus, cela s'applique également aux autres types chargés à partir de nib (ou n'importe où ailleurs aprèsinit
): chaînes, références aux sous-vues, etc.let foo? = 42
plutôtlet foo! = 42
). Cela ne règle pas cela. (Cela peut être une réponse pertinente à propos des options, faites attention, mais pas à propos des options implicitement déballées qui sont un animal différent / apparenté.)Apple donne un excellent exemple dans The Swift Programming Language -> Automatic Reference Counting -> Resolving Strong Reference Cycles between Class Instances -> Unown References and Implicitly Unwrapped Optional Properties
la source
La justification des options implicites est plus facile à expliquer en examinant d'abord la justification du déballage forcé.
Déballage forcé d'une option (implicite ou non), en utilisant le! , signifie que vous êtes certain que votre code n'a pas de bogues et que l'option a déjà une valeur là où elle est déballée. Sans le ! , vous affirmeriez probablement avec une liaison facultative:
ce qui n'est pas aussi agréable que
Maintenant, les options implicites vous permettent d'exprimer une option que vous attendez d'avoir toujours une valeur une fois dépliée, dans tous les flux possibles. Cela va donc encore plus loin en vous aidant - en relâchant l'exigence d'écrire le! pour déballer à chaque fois, et vous assurer que le runtime continuera à générer des erreurs au cas où vos hypothèses sur le flux seraient erronées.
la source
init
(que vous pourriez même ne pas implémenter), mais elles ' re garanti pour être réglé aprèsawakeFromNib
/viewDidLoad
.Si vous savez avec certitude, une valeur renvoyée par un optionnel au lieu de
nil
, Optionally Unwrapped Optionals utilise pour attraper directement ces valeurs des optionnels et non optionnels ne peut pas.Voilà donc la différence entre l'utilisation de
let someString : String!
etlet someString : String
la source
Je pense que
Optional
c'est un mauvais nom pour cette construction qui déroute beaucoup de débutants.D'autres langages (Kotlin et C # par exemple) utilisent le terme
Nullable
, et cela le rend beaucoup plus facile à comprendre.Nullable
signifie que vous pouvez affecter une valeur nulle à une variable de ce type. Donc, si c'est le casNullable<SomeClassType>
, vous pouvez lui attribuer des valeurs nulles, si c'est justeSomeClassType
, vous ne pouvez pas. Voilà comment Swift fonctionne.Pourquoi les utiliser? Eh bien, parfois, vous devez avoir des valeurs nulles, c'est pourquoi. Par exemple, lorsque vous savez que vous voulez avoir un champ dans une classe, mais que vous ne pouvez l'attribuer à rien lorsque vous créez une instance de cette classe, mais vous le ferez plus tard. Je ne donnerai pas d'exemples, car les gens les ont déjà fournis ici. J'écris juste ceci pour donner mes 2 cents.
Btw, je vous suggère de voir comment cela fonctionne dans d'autres langages, comme Kotlin et C #.
Voici un lien expliquant cette fonctionnalité dans Kotlin: https://kotlinlang.org/docs/reference/null-safety.html
D'autres langages, comme Java et Scala, ont des
Optional
s, mais ils fonctionnent différemment deOptional
s dans Swift, car les types de Java et Scala sont tous annulables par défaut.Dans l'ensemble, je pense que cette fonctionnalité aurait dû être nommée
Nullable
dans Swift, et nonOptional
...la source
Implicitly Unwrapped Optional
est un sucre syntaxique carOptional
cela n'oblige pas un programmeur à déballer une variable. Il peut être utilisé pour une variable qui ne peut pas être initialisée pendanttwo-phase initialization process
et implique non nul. Cette variable se comporte comme non nulle mais est en fait une variable facultative . Un bon exemple est - les points de vente d'Interface BuilderOptional
sont généralement préférablesla source