Question Pourquoi créer "des options implicitement non emballées", car cela implique que vous savez qu'il existe une valeur?


Pourquoi voudriez-vous créer un "facultatif implicitement non-emballé" vs créer juste une variable régulière ou constante? Si vous savez qu'il peut être déballé avec succès, alors pourquoi créer une option en premier lieu? Par exemple, pourquoi est-ce:

let someString: String! = "this is the string"

va être plus utile que:

let someString: String = "this is the string"

Si "les optionnels indiquent qu'une constante ou une variable est autorisée à avoir" aucune valeur "", mais "parfois il est clair d'après la structure d'un programme qu'un optionnel aura toujours une valeur après que cette valeur est définie", quel est le point de ce qui en fait une option en premier lieu? Si vous savez qu'un optionnel aura toujours une valeur, cela ne le rend-il pas optionnel?


441
2018-06-03 04:09


origine


Réponses:


Considérons le cas d'un objet qui peut avoir des propriétés nulles pendant qu'il est construit et configuré, mais qui est immuable et non nul après (NSImage est souvent traité de cette façon, bien que dans son cas il soit toujours utile de muter parfois). Les options optionnelles non emballées permettraient de bien nettoyer son code, avec une perte de sécurité relativement faible (tant que la garantie serait maintenue, elle serait sûre).

(Edit) Pour être clair cependant: les optionnels réguliers sont presque toujours préférables.


113
2018-06-03 04:15



Avant de pouvoir décrire les cas d'utilisation pour les options implicitement non enveloppées, vous devriez déjà comprendre ce que sont les options et les options optionnelles non enveloppées dans Swift. Si vous ne le faites pas, je vous recommande d'abord de lire mon article sur les optionnels

Quand utiliser une option implicitement déballée

Il y a deux raisons principales que l'on créerait une option implicitement Unwrapped. Tous ont à voir avec la définition d'une variable qui ne sera jamais accessible quand nil car sinon, le compilateur Swift vous forcera toujours à déballer explicitement un optionnel.

1. Une constante qui ne peut pas être définie pendant l'initialisation

Chaque constante 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 accédée.

L'utilisation d'une variable optionnelle contourne ce problème car une option est automatiquement initialisée avec nil et la valeur qu'il contiendra finalement sera immuable. Cependant, il peut être pénible de déployer constamment une variable dont vous savez qu’elle n’est pas nulle. Les options optionnelles non enveloppées offrent les mêmes avantages que les options, avec l'avantage supplémentaire de ne pas avoir à les déballer explicitement partout.

Un bon exemple est celui où une variable membre ne peut pas être initialisée dans une sous-classe UIView tant que la vue n'est pas 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 tant que la vue n'est pas chargée, 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éballée sans objet partout dans votre classe, vous pouvez le déclarer en tant que facultatif Unwrapped Facultatif.

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 à fonctionner si une variable est nil une fois accédé, ce serait une perte de temps de le tester pour nil. Normalement, si vous avez une condition qui doit absolument être vraie pour que votre application continue à fonctionner, vous utiliserez un assert. Un optionnel Unwrapped Facultatif a un assert pour nil intégré juste dedans. Même dans ce cas, il est souvent judicieux de déballer les options et d'utiliser une affirmation plus descriptive si elle est nulle.

Quand ne pas utiliser un facultatif implicitement non emballé

1. Variables membres calculés

Parfois, vous avez une variable membre qui ne devrait jamais être nulle, mais elle ne peut pas être définie sur la valeur correcte lors de l'initialisation. Une solution consiste à utiliser implicitement unwrapped facultatif, mais une meilleure façon est d'utiliser une variable paresseuse:

class FileSystemItem {
}

class Directory : FileSystemItem {
    lazy var contents : [FileSystemItem] = {
        var loadedContents = [FileSystemItem]()
        // load contents and append to loadedContents
        return loadedContents
    }()
}

Maintenant, la variable membre contents n'est pas initialisé jusqu'à la première fois qu'il est accédé. Cela donne à la classe une chance d'entrer dans l'état correct avant de calculer la valeur initiale.

Remarque: Cela peut sembler contredire le # 1 d'en haut. Cependant, il y a une distinction importante à faire. le buttonOriginalWidth ci-dessus doit être défini pendant viewDidLoad pour empêcher quiconque de modifier la largeur des boutons avant l’accès à la propriété.

2. Partout ailleurs

Pour l'essentiel, les options optionnelles non enveloppées doivent être évitées, car si elles sont utilisées par erreur, l'application entière tombera en panne 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 option facultative. Développer une variable qui n'est jamais nil certainement pas très mal.


426
2017-07-05 05:03



Les options optionnelles non dépliées sont utiles pour présenter une propriété comme facultative alors qu'en réalité, elle doit être optionnelle sous les couvertures. Ceci est souvent nécessaire pour "faire le nœud" entre deux objets liés, chacun ayant besoin d'une référence à l'autre. Cela a du sens quand aucune référence n'est réellement optionnel, mais l'un d'eux doit être nul lorsque la paire est en cours d'initialisation.

Par exemple:

// These classes are buddies that never go anywhere without each other
class B {
    var name : String
    weak var myBuddyA : A!
    init(name : String) {
        self.name = name
    }
}

class A {
    var name : String
    var myBuddyB : B
    init(name : String) {
        self.name = name
        myBuddyB = B(name:"\(name)'s buddy B")
        myBuddyB.myBuddyA = self
    }
}

var a = A(name:"Big A")
println(a.myBuddyB.name)   // prints "Big A's buddy B"

Tout B instance devrait toujours avoir un valide myBuddyA référence, de sorte que nous ne voulons pas que l'utilisateur le traite comme facultatif, mais nous avons besoin qu'il soit facultatif afin que nous puissions construire un B avant que nous ayons un A 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 conception médiocre. Si vous vous trouvez en vous fondant sur des options implicitement déballées, vous devriez probablement envisager de refactoriser pour éliminer les interdépendances.


52
2018-06-03 22:44



Les options implicitement déballées sont un compromis pragmatique pour rendre le travail dans un environnement hybride qui doit interagir avec les frameworks Cocoa existants et leurs conventions plus agréables, tout en permettant une migration progressive vers un paradigme de programmation plus sûr - sans pointeur nul - imposé par le compilateur Swift.

Livre rapide, en Les baseschapitre, section Options optionnelles implicitement non emballées dit:

Les optionnels implicitement déballés sont utiles lorsqu'une valeur facultative est confirmée pour exister immédiatement après la définition de l'option et que l'on peut certainement supposer qu'elle existe à chaque point par la suite. La principale utilisation des options optionnellement déroulées implicitement dans Swift est lors de l'initialisation de la classe, comme décrit dans Références sans cote et propriétés optionnelles implicitement déballées.   
...
  Vous pouvez penser à un optionnel implicitement déballé comme donnant la permission à l'optionnel d'être déballé automatiquement chaque fois qu'il est utilisé. Plutôt que de placer un point d'exclamation après le nom facultatif chaque fois que vous l'utilisez, vous placez un point d'exclamation après le type facultatif lorsque vous le déclarez.

Cela revient à utiliser des cas où le non-nil-ness de 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, le UIViewController 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 la viewDidLoad() vous pouvez supposer que les propriétés existent généralement. Sinon, pour satisfaire le compilateur, vous devez utiliser le déballage forcé, liaison facultative  ou chaînage optionnel  seulement pour obscurcir l'objectif principal du code.

Au-dessus de la partie du livre de Swift se rapporte également à la Comptage automatique des références chapitre:

Cependant, il existe un troisième scénario, dans lequel les deux propriétés doivent toujours avoir une valeur, et aucune propriété ne doit jamais être nil une fois l'initialisation terminée. Dans ce scénario, il est utile de combiner une propriété non propriétaire sur une classe avec une propriété optionnelle implicitement non compressée dans l'autre classe.

Cela permet d'accéder directement aux deux propriétés (sans déballage facultatif) une fois l'initialisation terminée, tout en évitant un cycle de référence.

Cela se résume aux caprices de ne pas être un langage récupéré, donc la rupture des cycles de rétention est sur vous en tant que programmeur et les optionnels implicitement déballés sont un outil pour cacher cette bizarrerie.

Cela couvre la question "Quand utiliser les optionnels implicitement déballés dans votre code?" En tant que développeur d'applications, vous les rencontrerez généralement dans les signatures de méthodes des bibliothèques écrites en Objective-C, qui n'ont pas la capacité d'exprimer des types facultatifs.

De Utiliser Swift avec Cocoa et Objective-C, section Travailler avec nil:

Comme Objective-C ne garantit pas qu'un objet est non-nul, Swift rend toutes les classes dans les types d'argument et les types de retour facultatifs dans les API Objective-C importées. Avant d'utiliser un objet Objective-C, vous devez vérifier qu'il n'est pas manquant.

Dans certains cas, vous pourriez être absolument certain qu'une méthode ou une propriété Objective-C ne retourne jamais nilréférence d'objet. Pour rendre les objets de ce scénario spécial plus pratiques, Swift importe les types d'objet implicitement déballés optionnels. Les types facultatifs déroulés implicitement incluent toutes les fonctions de sécurité des types facultatifs. De plus, vous pouvez accéder directement à la valeur sans vérifier nil ou déballer vous-même. Lorsque vous accédez à la valeur dans ce type de type facultatif sans la désarchiver en toute sécurité en premier, l'option facultativement non compressée vérifie si la valeur est manquante. Si la valeur est manquante, une erreur d'exécution se produit. Par conséquent, vous devez toujours vérifier et déballer vous-même une option implicitement déballée, sauf si vous êtes certain que la valeur ne peut pas être manquante.

... et plus loin ici dragons


32
2018-06-12 14:06



Les exemples simples d'une ligne (ou de plusieurs lignes) ne couvrent pas très bien le comportement des options - oui, si vous déclarez une variable et lui donnez une valeur immédiatement, il n'y a pas de raison d'optionnel.

Le meilleur cas que j'ai vu jusqu'ici est la configuration qui se produit après l'initialisation de l'objet, suivie par l'utilisation qui est "garantie" pour suivre cette configuration, par ex. dans un contrôleur de vue:

class MyViewController: UIViewController {

    var screenSize: CGSize?

    override func viewDidLoad {
        super.viewDidLoad()
        screenSize = view.frame.size
    }

    @IBAction printSize(sender: UIButton) {
        println("Screen size: \(screenSize!)")
    }
}

Nous savons printSize sera appelé après le chargement de la vue - c'est une méthode d'action reliée à un contrôle dans cette vue, et nous nous sommes assurés de ne pas l'appeler autrement. Nous pouvons donc nous épargner des vérifications / liaisons facultatives avec le !. Swift ne peut pas reconnaître cette garantie (au moins jusqu'à ce qu'Apple résout le problème d'arrêt), vous dites donc au compilateur qu'il existe.

Cela brise la sécurité de type dans une certaine mesure, cependant. Quel que soit l'endroit où vous avez implicitement déballé une option est un endroit où votre application peut tomber en panne si votre "garantie" ne tient pas toujours, c'est donc une fonctionnalité à utiliser avec parcimonie. D'ailleurs, en utilisant ! tout le temps donne l'impression que vous criez, et personne n'aime ça.


18
2018-06-03 04:26



Apple donne un bon exemple dans Le langage de programmation Swift -> Comptage automatique des références -> Résolution de cycles de référence forts entre les instances de classe -> Références sans cote et propriétés optionnelles implicitement déballées

class Country {
    let name: String
    var capitalCity: City! // Apple finally correct this line until 2.0 Prerelease (let -> var)
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

L'initialiseur pour City est appelé à partir de l'initialisateur pour Country. Cependant, l'initialiseur pour Country ne peut pas passer self au City initialiseur jusqu'à une nouvelle Country instance est entièrement initialisée, comme décrit dans Initialisation en deux phases.

Pour faire face à cette exigence, vous déclarez capitalCity propriété de Country en tant que propriété facultative implicitement déballée.


14
2017-08-17 09:47



La raison d'être des options optionnelles implicites est plus facile à expliquer en examinant d'abord la logique du déballage forcé.

Déballage forcé d'un optionnel (implicite ou non), en utilisant le! opérateur, signifie que vous êtes certain que votre code n'a pas de bogues et que l'option a déjà une valeur où elle est déballée. Sans le ! opérateur, vous vous contentez probablement d’affirmer avec une liaison optionnelle:

 if let value = optionalWhichTotallyHasAValue {
     println("\(value)")
 } else {
     assert(false)
 }

ce qui n'est pas aussi bien que

println("\(value!)")

Maintenant, les options implicites vous permettent d'exprimer une option que vous attendez toujours avoir une valeur lorsqu'il est déballé, dans tous les flux possibles. Donc, il va juste un peu plus loin en vous aidant - en relaxant l'exigence d'écrire le! pour déballer chaque fois, et s'assurer que le temps d'exécution continuera d'erreur dans le cas où vos hypothèses sur le flux sont erronées.


3
2018-06-03 22:59



Si vous savez à coup sûr, un retour de valeur d'un optionnel au lieu de nil, Options optionnelles implicitement non emballées utiliser pour attraper directement ces valeurs d'optionals et non optionnels ne peuvent pas.

//Optional string with a value
let optionalString: String? = "This is an optional String"

//Declaration of an Implicitly Unwrapped Optional String
let implicitlyUnwrappedOptionalString: String!

//Declaration of a non Optional String
let nonOptionalString: String

//Here you can catch the value of an optional
implicitlyUnwrappedOptionalString = optionalString

//Here you can't catch the value of an optional and this will cause an error
nonOptionalString = optionalString

Donc, c'est la différence entre l'utilisation de

let someString : String!  et  let someString : String


2
2018-01-14 16:52