Pourquoi créer "des options implicitement non emballées", puisque cela implique que vous savez qu'il y a une valeur?

pourquoi créeriez-vous un vs" implicitement Unwrapped Optional " en créant juste une variable ou une constante régulière? Si vous savez qu'il peut être déballé avec succès, alors pourquoi créer une option en premier lieu? Par exemple, pourquoi:

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

va être plus utile que:

let someString: String = "this is the string"

si "les options indiquent qu'une constante ou une variable est autorisée à n'avoir" aucune valeur", mais " parfois il est clair de la structure d'un programme qu'un optionnel aura toujours une valeur après que cette valeur est définie pour la première fois", à quoi cela sert-il de le rendre optionnel en premier lieu? Si vous savez qu'un optionnel aura toujours une valeur, cela ne le rend-il pas optionnel?

449
demandé sur Seto Elkahfi 2014-06-03 08:09:33
la source

8 ответов

considère 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 par la suite (NSImage est souvent traité de cette façon, bien que dans son cas il soit encore utile de muter parfois). Implicitement déballé options nettoyer son code une bonne affaire, avec relativement peu de pertes de sécurité (tant que le garantir, il serait à l'abri).

(Edit) pour être clair cependant: les options régulières sont presque toujours préférables.

114
répondu Catfish_Man 2014-06-03 08:15:58
la source

avant que je puisse décrire les cas d'utilisation pour les optionnels implicitement non enveloppés, vous devriez déjà comprendre ce que sont les optionnels et les optionnels implicitement non enveloppés dans Swift. Si vous ne le faites pas, je vous recommande d'abord lu mon article sur les optionnels

Quand Utiliser Une Option Implicitement Déballée

il y a deux raisons principales pour lesquelles l'une créerait une option implicitement non emballée. Tous ont à voir avec la définition d'un variable qui ne sera jamais accessible lorsque nil , car autrement, le compilateur Swift vous forcera toujours à désactiver explicitement une option.

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é à sa valeur correcte lors de l'initialisation, mais il peut encore être garanti d'avoir une valeur avant d'être accéder.

en utilisant une variable optionnelle permet de contourner ce problème parce qu'une optionnelle est automatiquement initialisée avec nil et la valeur qu'elle contiendra éventuellement sera toujours immuable. Cependant, il peut être une douleur d'être constamment déballer une variable que vous savez à coup sûr n'est pas nul. Les optionnels implicitement non enveloppés obtiennent les mêmes avantages Qu'un optionnel avec l'avantage supplémentaire qu'il n'est pas nécessaire de le déballer explicitement partout.

un bon exemple de ceci est quand 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 jusqu'à ce que la vue charge, mais vous savez que awakeFromNib sera appelé avant toute autre méthode sur la vue (autre que l'initialisation). Au lieu de forcer la valeur à être explicitement déballée inutilement dans toute votre classe, vous pouvez la déclarer comme une Déballé En Option.

2. Lorsque votre application ne peut pas récupérer D'une Variable étant nil

cela devrait être extrêmement rare, mais si votre application ne peut pas continuer à fonctionner si une variable est nil lors de l'accès, ce serait une perte de temps de prendre la peine de le tester pour nil . Normalement, si vous avez une condition qui doit absolument être vrai pour votre application de fonctionner, vous pouvez utiliser un assert . Une option implicitement déballée a une affirmer sans intégré à. Même alors, il est souvent bon de les déballer l'option et utiliser un plus descriptif affirmer si c'est nul.

Quand Ne Pas Utiliser Un Implicitement Déballé Option

1. Paresseusement Membre Calculé Variables

parfois, vous avez une variable membre qui ne devrait jamais être nulle, mais qui ne peut pas être réglée à la bonne valeur lors de l'initialisation. Une solution consiste à utiliser une option implicitement déballée, 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 initialisée que la première fois qu'on y accède. Cela donne à la classe une chance d'entrer dans le bon état avant de calculer la valeur initiale.

Note: ceci peut sembler contredire le numéro 1 ci-dessus. Toutefois, il y a une distinction importante à faire. Le buttonOriginalWidth ci-dessus doit être réglé pendant viewDidLoad à empêcher quiconque de changer la largeur des boutons avant que la propriété est accessible.

2. Partout Ailleurs

pour la plupart, les options implicitement déballées devraient être évitées parce que si utilisé par erreur, votre application entière se plantera quand il est accédé tandis que nil . Si vous n'êtes jamais sûr de savoir si une variable peut être nulle, utilisez toujours par défaut une option normale. Déballer une variable qui n'est jamais nil ne fait certainement pas très mal beaucoup.

432
répondu drewag 2018-03-10 08:05:11
la source

les options implicitement déballées sont utiles pour présenter une propriété comme non-optionnelle alors qu'en réalité elle doit être optionnelle sous les covers. Cela est souvent nécessaire pour "attacher le noeud" entre deux objets que chacun besoin d'une référence à l'autre. Cela a du sens quand aucune référence n'est réellement optionnel, mais l'une d'entre elles doit être nulle pendant que la paire est initialisée.

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"

Any B instance devrait toujours avoir une référence valide myBuddyA , donc nous ne voulons pas faire l'utilisateur le traiter comme optionnel, mais nous avons besoin qu'il soit optionnel afin que nous puissions construire un B avant que nous ayons un A à se référer.

cependant! Ce type d'exigence de référence mutuelle est souvent une indication d'accouplement serré et de mauvaise conception. Si vous vous trouvez à compter sur implicitement des options non enveloppées, vous devriez probablement considérer refactoring pour éliminer la croix-dépendances.

54
répondu n8gray 2014-07-28 22:30:25
la source

les options implicitement Non enveloppées sont un compromis pragmatique pour rendre le travail dans un environnement hybride qui doit interférer avec les cadres cacaoyer existants et leurs conventions plus agréables, tout en permettant une migration progressive vers un paradigme de programmation plus sûr - sans pointeurs nuls - appliqué par le compilateur Swift.

Swift livre, dans Les Bases , le chapitre, la section Implicitement Déballé Options dit:

les options implicitement déballées sont utiles lorsqu'il est confirmé que la valeur d'une option existe immédiatement après que l'option a été définie pour la première fois et qu'elle peut certainement être supposée exister à chaque point par la suite. L'utilisation principale des options implicitement non emballées dans Swift se fait lors de l'initialisation de la classe, comme décrit dans références non acquises et Propriétés facultatives implicitement non emballées .

...

Vous pouvez penser à un implicitement déballé option que de donner l'autorisation pour l'option d'être déballé automatiquement lorsqu'il est utilisé. Plutôt que de placer un point d'exclamation après l'option de nom à chaque fois que vous l'utilisez, vous placez un point d'exclamation après l'option du type lorsque vous déclarez.

il s'agit d'utiliser des cas où le non - nil - ness de biens est établi via la convention d'utilisation, et ne peut pas être appliqué par le compilateur pendant l'initialisation de classe. Par exemple, les propriétés UIViewController qui sont initialisées à partir de NIBs ou Storyboards, où l'initialisation est divisée en phases séparées, mais après le viewDidLoad() vous pouvez supposer que les propriétés existent généralement. Dans le cas contraire, pour satisfaire le compilateur, il fallait utiliser le déballage forcé , option obligatoire ou chaînage facultatif seulement pour obscurcir le but principal du code.

ci-dessus une partie du livre Swift se réfère également au Comptage automatique de référence chapitre :

cependant, il existe un troisième scénario, dans lequel les deux propriétés devraient toujours avoir une valeur, et aucune propriété ne devrait jamais être nil une fois l'initialisation terminée. Dans ce scénario, il est utile de combiner une propriété non-possédée sur une classe avec une propriété optionnelle implicitement non-emballée sur 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 bizarreries de ne pas être un langage ramassé, donc la rupture des cycles de conservation est sur vous en tant que programmeur et implicitement non enveloppés optionnels sont un outil pour cacher cette bizarrerie.

qui couvre le " quand utiliser des 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 de bibliothèques écrites dans Objective-C, qui n'a pas la capacité d'exprimer des types optionnels.

à Partir de l'Utilisation de Swift avec Cocoa et Objective-C, section Travailler avec nil :

étant donné que Objective-C ne garantit pas qu'un objet n'est pas nul, Swift rend toutes les catégories de types d'arguments et de types de déclarations facultatives dans les IPA importés de Objective-C. Avant d'utiliser un objet Objective-C, vous devriez vérifier qu'il ne manque pas.

dans certains cas, vous pourriez être absolument certain qu'une méthode Objective-C ou un bien ne retourne jamais a nil référence de l'objet. Pour rendre les objets de ce scénario spécial plus faciles à utiliser, Swift importe des types d'objets comme , des options implicitement déballées . Les types optionnels implicitement déroulés comprennent toutes les caractéristiques de sécurité des types optionnels. De plus, vous pouvez accéder directement à la valeur sans vérifier nil ou la déballer vous-même. Lorsque vous accédez à la valeur de ce type de type optionnel sans la déballer en toute sécurité en premier, la valeur implicitement déballée contrôle optionnel si la valeur est manquante. Si la valeur est manquante, une erreur d'exécution se produit. En conséquence, vous devriez toujours vérifier et déballer vous-même une option implicitement déballée, à moins que vous ne soyez sûr que la valeur ne peut pas être manquante.

...et au-delà de là se trouvent dragons

34
répondu Palimondo 2014-06-16 19:39:27
la source

des exemples simples D'une ligne (ou de plusieurs lignes) ne couvrent pas très bien le comportement des optionnels-Oui, si vous déclarez une variable et lui fournissez une valeur immédiatement, il n'y a aucun point dans un optionnel.

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 qui est "garantie" de suivre cette configuration, par exemple 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 la vue est chargée - il s'agit d'une méthode d'action accrochée à un contrôle à l'intérieur de cette vue, et nous avons fait en sorte de ne pas l'appeler autrement. Nous pouvons donc nous épargner quelques vérifications/reliures optionnelles avec le ! . Swift ne peut pas reconnaître cette garantie (au moins jusqu'à ce Qu'Apple résolve le problème de halting), donc vous dites au compilateur qu'il existe.

cela brise la sécurité de type dans une certaine mesure, cependant. Anyplace vous avez un implicitement non enveloppé optionnel est un endroit votre application peut planter si votre "garantie" ne tient pas toujours, il est donc une fonctionnalité à utiliser avec parcimonie. En plus, utiliser ! tout le temps donne l'impression que tu cries, et personne n'aime ça.

18
répondu rickster 2014-06-03 08:26:15
la source

Apple donne un bon exemple dans le langage de programmation Swift -> comptage de référence automatique -> résolution de Cycles de référence forts entre les Instances de classe - > références non-acquises et propriétés optionnelles implicitement non emballé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é de l'intérieur de initialiseur pour Country . Cependant, l'initialiseur pour Country ne peut pas passer self à l'initialiseur City jusqu'à ce qu'une nouvelle instance Country soit entièrement initialisée, comme décrit dans initialisation deux phases .

pour répondre à cette exigence, vous déclarez la capitalCity propriété de Country comme une propriété optionnelle implicitement déballée.

14
répondu fujianjin6471 2017-01-04 09:19:11
la source

il est plus facile d'expliquer la raison d'être des options implicites en examinant d'abord la raison d'être 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 l'optionnel a déjà une valeur où il est déballé. Sans le ! opérateur, vous affirmeriez probablement avec une reliure optionnelle:

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

qui n'est pas aussi beau que

println("\(value!)")

maintenant, les options implicites vous permettent d'exprimer avoir une option que vous attendez à toujours d'avoir une valeur quand déballée, dans tous les flux possibles. Donc, il va juste un peu plus loin en vous aidant - en assouplissant l'exigence d'écrire le ! déballer à chaque fois, et s'assurer que l'exécution sera encore erreur au cas où vos hypothèses sur le flux sont fausses.

3
répondu Danra 2014-06-04 02:59:36
la source

si vous êtes sûr, un retour de valeur d'un optionnel au lieu de nil , implicitement non enveloppé optionnels utiliser pour attraper directement ces valeurs d'optionnels et non optionnels ne peut 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
répondu enadun 2017-01-14 19:52:38
la source

Autres questions sur