Question Que signifie un point d'exclamation dans la langue Swift?


Le guide du langage de programmation Swift a l'exemple suivant:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { println("\(name) is being deinitialized") }
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    var tenant: Person?
    deinit { println("Apartment #\(number) is being deinitialized") }
}

var john: Person?
var number73: Apartment?

john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)

//From Apple's “The Swift Programming Language” guide (https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html)

Ensuite, lors de l'attribution de l'appartement à la personne, ils utilisent un point d'exclamation pour "dérouler l'instance":

john!.apartment = number73

Que signifie "déballer l'instance"? Pourquoi est-ce nécessaire? En quoi est-ce différent de simplement faire ce qui suit:

john.apartment = number73

Je suis très nouveau dans la langue Swift. J'essaie juste d'obtenir les bases.


METTRE À JOUR:
La grande partie du casse-tête qui me manquait (pas directement dans les réponses - du moins pas au moment d'écrire ceci) est celle-ci quand vous faites ce qui suit:

var john: Person?

cela ne veut pas dire que "john est de type Person et il pourrait être nul ", comme je le pensais au départ. J'étais simplement mal compris que Person et Person? sont des types complètement séparés. Une fois que j'ai compris cela, tous les autres ?, ! la folie, et les grandes réponses ci-dessous, ont beaucoup plus de sens.


478
2018-06-03 14:52


origine


Réponses:


Que signifie "déballer l'instance"? Pourquoi est-ce nécessaire?

Pour autant que je puisse travailler (c'est très nouveau pour moi aussi) ...

Le terme "enveloppé" implique que nous devrions penser à une variable optionnelle comme cadeau, enveloppée dans du papier brillant, qui pourrait (malheureusement!) être vide.

Lorsque "wrapped", la valeur d'une variable optionnelle est une enum avec deux valeurs possibles (un peu comme un booléen). Cette énumération indique si la variable contient une valeur (Some(T)), ou pas (None).

S'il y a une valeur, cela peut être obtenu en "déroulant" la variable (en obtenant le T de Some(T)).

Comment est john!.apartment = number73 différent de john.apartment = number73? (Paraphrasé)

Si vous écrivez le nom d’une variable optionnelle (par exemple, texte john, sans le !), cela fait référence à l’enum "enveloppé" (Some / None), pas à la valeur elle-même (T). Alors john n'est pas une instance de Personet il n'a pas de apartment membre:

john.apartment
// 'Person?' does not have a member named 'apartment'

L'actuel Person la valeur peut être déballée de différentes manières:

  • "déballage forcé": john! (donne le Person valeur si elle existe, erreur d'exécution si elle est nulle
  • "liaison facultative": if let p = john { println(p) } (exécute le println si la valeur existe)
  • "chaînage facultatif": john?.learnAboutSwift() (exécute cette méthode créée si la valeur existe)

Je suppose que vous choisissez l'une de ces façons de déballer, en fonction de ce qui devrait arriver dans le cas nul et de la probabilité que cela se produise. Cette conception du langage oblige à traiter explicitement le cas nul, ce qui, je suppose, améliore la sécurité par rapport à Obj-C (où il est facile d'oublier de traiter le cas nul).

Mettre à jour:

Le point d'exclamation est également utilisé dans la syntaxe pour déclarer "Optionals non enveloppés implicitement".

Dans les exemples jusqu'à présent, le john la variable a été déclarée comme var john:Person?, et c'est un optionnel. Si vous voulez la valeur réelle de cette variable, vous devez la déballer en utilisant l'une des trois méthodes ci-dessus.

Si elle a été déclarée comme var john:Person! au lieu de cela, la variable serait une option implicitement non emballée (voir la section avec ce titre dans le livre d'Apple). Il n'est pas nécessaire de déballer ce type de variable lors de l'accès à la valeur, et johnpeut être utilisé sans syntaxe supplémentaire. Mais le livre d'Apple dit:

Les options optionnelles non développées ne doivent pas être utilisées lorsqu'il est possible qu'une variable devienne nulle ultérieurement. Utilisez toujours un type facultatif normal si vous devez vérifier une valeur nulle pendant la durée de vie d'une variable.

Mise à jour 2:

L'article "Caractéristiques intéressantes de Swift"par Mike Ash donne une certaine motivation pour les types optionnels, je pense que c'est génial, l'écriture claire.

Mise à jour 3:

Un autre article utile sur la implicitement déballé optionnel utiliser pour le point d'exclamation: "Swift et le dernier mile»par Chris Adamson L'article explique qu'il s'agit d'une mesure pragmatique utilisée par Apple pour déclarer les types utilisés par leurs frameworks Objective-C qui peuvent contenir nil. ?) ou implicitement déballé (en utilisant !) est "un compromis entre la sécurité et la commodité". Dans les exemples donnés dans l'article, Apple a choisi de déclarer les types comme étant implicitement déballés, ce qui rend le code d'appel plus pratique, mais moins sûr.

Peut-être qu'Apple pourrait-il passer au peigne fin leurs cadres, en supprimant l'incertitude des paramètres implicitement déballés ("probablement jamais nuls") et en les remplaçant par optionnels ("certainement pourrait être nul en particulier [circonstances heureusement documentées]) ou standard non -optional ("is never nil") déclarations, basées sur le comportement exact de leur code Objective-C.


492
2018-06-03 17:04



Voici ce que je pense être la différence:

var john: Person?

Signifie que John peut être nul

john?.apartment = number73

Le compilateur interprétera cette ligne comme:

if john != nil {
    john.apartment = number73
}

Tandis que

john!.apartment = number73

Le compilateur interprétera cette ligne simplement:

john.apartment = number73

Par conséquent, en utilisant! va dérouler l'instruction if, et la rendre plus rapide, mais si john est nul, une erreur d'exécution se produira.

Donc wrap ici ne signifie pas qu'il est enveloppé dans la mémoire, mais cela signifie qu'il est enveloppé de code, dans ce cas il est enveloppé avec une instruction if, et parce qu'Apple prête une attention particulière aux performances en runtime, il veut vous donner un moyen de Faites en sorte que votre application fonctionne avec les meilleures performances possibles.

Mettre à jour:

Revenons à cette réponse après 4 ans, car j'ai eu la plus haute réputation dans Stackoverflow :) J'ai mal compris le sens de déballer à ce moment-là. Maintenant, après 4 ans, je crois que le sens de déballer ici est d'étendre le code de sa forme compacte d'origine. Aussi, cela signifie enlever le flou autour de cet objet, car nous ne sommes pas sûrs par définition qu'il est nul ou pas. Tout comme la réponse d'Ashley ci-dessus, pensez-y comme un cadeau qui ne peut rien contenir. Mais je pense toujours que le déballage est le déballage du code et non le déballage basé sur la mémoire en utilisant enum.


116
2018-06-20 04:16



TL; DR

Que signifie un point d'exclamation dans la langue Swift?

Le point d'exclamation dit effectivement: «Je sais que cette option   a définitivement une valeur; s'il vous plaît utilisez-le. "Ceci est connu sous le nom de déballage forcé de la valeur de l'option:

Exemple

let possibleString: String? = "An optional string."
print(possibleString!) // requires an exclamation mark to access its value
// prints "An optional string."

let assumedString: String! = "An implicitly unwrapped optional string."
print(assumedString)  // no exclamation mark is needed to access its value
// prints "An implicitly unwrapped optional string."

La source: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399


62
2017-07-17 02:22



Si john était un var optionnel (déclaré ainsi)

var john: Person?

alors il serait possible que John n'ait aucune valeur (en langage ObjC, valeur nulle)

Le point d'exclamation indique au compilateur "Je sais que cela a une valeur, vous n'avez pas besoin de le tester". Si vous ne voulez pas l'utiliser, vous pouvez le tester conditionnellement:

if let otherPerson = john {
    otherPerson.apartment = number73
}

L'intérieur n'évaluera que si John a une valeur.


37
2018-06-03 15:00



john est une option var. Ainsi peut être contient un nil valeur. Pour vous assurer que la valeur n'est pas nulle, utilisez un ! à la fin de var prénom.

De la documentation

"Une fois que vous êtes sûr que l'option contient une valeur, vous pouvez accéder à sa valeur sous-jacente en ajoutant un point d'exclamation (!) À la fin du nom de l'option. Le point d'exclamation dit effectivement: "Je sais que cette option a bien une valeur; veuillez l'utiliser s'il vous plaît. "

Une autre façon de vérifier la valeur non nulle est

    if let j = json {
        // do something with j
    }

24
2018-06-03 14:54



Une perspective de grande image à ajouter aux autres réponses utiles mais plus détaillées:

Dans Swift, le point d'exclamation apparaît dans plusieurs contextes:

  • Déballage forcé: let name = nameLabel!.text
  • Implicitement déballés optionnels: var logo: UIImageView!
  • Coulée forcée: logo.image = thing as! UIImage
  • Exceptions non gérées: try! NSJSONSerialization.JSONObjectWithData(data, [])

Chacun d'entre eux est une construction de langage différent avec une signification différente, mais ils ont tous trois choses en commun:

1. Les points d'exclamation permettent de contourner les contrôles de sécurité de Swift au moment de la compilation.

Lorsque vous utilisez ! dans Swift, vous dites essentiellement: "Hé, compilateur, je sais que tu penses à une erreur pourrait arriver ici, mais je connaître avec la certitude totale qu'il ne le fera jamais. "

Tout code valide ne rentre pas dans la boîte du système de type de compilation de Swift - ou tout vérification de type statique du langage, d'ailleurs. Il y a des situations où vous pouvez logiquement prouver qu'une erreur ne se produira jamais, mais vous ne pouvez pas le prouver au compilateur. C'est pourquoi les concepteurs de Swift ont ajouté ces caractéristiques en premier lieu.

Cependant, chaque fois que vous utilisez !, vous excluez avoir un chemin de récupération pour une erreur, ce qui signifie que ...

2. Les points d'exclamation sont des accidents potentiels.

Un point d'exclamation dit aussi, "Hey Swift, je suis alors certain que cette erreur ne peut jamais arriver, il est préférable pour vous de planter toute mon application c'est à moi de coder un chemin de récupération pour cela. "

C'est une assertion dangereuse. Il pouvez Soyez le bon: dans un code critique où vous avez beaucoup réfléchi aux invariants de votre code, il se peut que la sortie fictive soit pire qu'un crash.

Cependant, quand je vois ! dans la nature, il est rarement utilisé de manière si attentive. Au lieu de cela, cela signifie trop souvent, "cette valeur était facultative et je n’ai pas vraiment réfléchi Pourquoi il pourrait être nul ou comment gérer correctement cette situation, mais en ajoutant ! l'a fait compiler ... alors mon code est correct, non? "

Méfiez-vous de l'arrogance du point d'exclamation. Au lieu…

3. Les points d'exclamation sont à utiliser avec parcimonie.

Chacun de ces ! construit a un ? contrepartie qui vous oblige à traiter le cas d'erreur / nul:

  • Déballage conditionnel: if let name = nameLabel?.text { ... }
  • Options: var logo: UIImageView?
  • Moulages conditionnels: logo.image = thing as? UIImage
  • Exceptions Nil-on-Fail: try? NSJSONSerialization.JSONObjectWithData(data, [])

Si vous êtes tenté d'utiliser !, il est toujours bon d'examiner attentivement pourquoi vous n'utilisez pas ? au lieu. Est écrasant votre programme vraiment la meilleure option si le ! opération échoue? Pourquoi cette valeur est-elle optionnelle / disponible?

Y a-t-il un chemin de récupération raisonnable que votre code pourrait prendre dans le cas zéro / erreur? Si c'est le cas, codez-le.

Si cela ne peut pas être nul, si l'erreur ne peut jamais arriver, alors y a-t-il un moyen raisonnable de retravailler votre logique pour que le compilateur le sache? Si oui, faites-le; votre code sera moins sujet aux erreurs.

Il y a des moments où il n'y a pas de moyen raisonnable de gérer une erreur, et ignorer simplement l'erreur - et donc procéder avec des données erronées - serait pire que de s'écraser. Ceux sont les temps d'utiliser le déballage de la force.

Je recherche périodiquement mon code entier pour ! et vérifie chaque utilisation de celui-ci. Très peu d'usages résistent à l'examen. (A ce jour, tout le cadre Siesta a exactement deux  instances de celui-ci.)

Cela ne veut pas dire que vous devriez jamais utilisation ! dans votre code - juste que vous devriez l'utiliser en pleine conscience, et ne jamais en faire l'option par défaut.


24
2017-10-28 19:30



Voici quelques exemples:

var name:String = "Hello World"
var word:String?

word est une valeur facultative. signifie qu'il peut ou non contenir une valeur.

word = name 

Ici name a une valeur pour que nous puissions l'assigner

var cow:String = nil
var dog:String!

dog est forcement déballé signifie qu'il doit contenir une valeur

dog = cow

L'application va planter car nous sommes assignés nil à déballer


16
2017-07-10 08:32