537 votes

Que signifie un point d'exclamation dans le langage 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)

Puis, lorsqu'ils attribuent l'appartement à la personne, ils utilisent un point d'exclamation pour "déballer l'instance" :

john!.apartment = number73

Que signifie "déballer l'instance" ? Pourquoi est-ce nécessaire ? Quelle est la différence avec les actions suivantes :

john.apartment = number73

Je suis très novice dans le langage Swift. J'essaie juste de me familiariser avec les bases.

UPDATE :
La grande pièce du puzzle qui me manquait (et qui n'est pas directement indiquée dans les réponses - du moins pas au moment où j'écris ces lignes) est que lorsque 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 initialement. Je comprenais simplement mal que Person y Person? sont des types complètement distincts. Une fois que j'ai compris ça, toutes les autres choses ? , ! la folie, et les excellentes réponses ci-dessous, ont eu beaucoup plus de sens.

548voto

Ashley Points 978

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

D'après ce que je sais (c'est tout nouveau pour moi aussi)...

Le terme "enveloppé" implique que nous devrions considérez une variable facultative comme un cadeau, emballé dans un papier brillant, qui pourrait (malheureusement !) être vide. .

Lorsqu'elle est "enveloppée", la valeur d'une variable facultative est un enum avec deux valeurs possibles (un peu comme un booléen). Cet enum décrit si la variable contient une valeur ( Some(T) ), ou non ( None ).

S'il y a une valeur, on peut l'obtenir en "déballant" la variable (en obtenant l'élément T de Some(T) ).

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

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

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

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

  • "déballage forcé" : john! (donne le Person 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 optionnel" : john?.learnAboutSwift() (exécute cette méthode inventé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 se passer dans le cas nil, et de la probabilité que cela soit le cas. Cette conception du langage force le cas nil à être traité explicitement, ce qui, je suppose, améliore la sécurité par rapport à Obj-C (où il est facile d'oublier de traiter le cas nil).

Mise à jour :

Le point d'exclamation est également utilisé dans la syntaxe de déclaration des "optionnels implicitement non enveloppés".

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

S'il était déclaré comme var john:Person! Au lieu de cela, la variable serait un Implicitly Unwrapped Optional (voir la section portant 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 la variable john peuvent être utilisés sans syntaxe supplémentaire. Mais le livre d'Apple dit :

Les optionnels implicitement déballés ne doivent pas être utilisés lorsqu'il est possible qu'une variable devienne nulle ultérieurement. Utilisez toujours un type d'option normal si vous devez vérifier la présence d'une valeur nulle pendant la durée de vie d'une variable.

Mise à jour 2 :

L'article " Fonctionnalités intéressantes de Swift " par Mike Ash donne une certaine motivation pour les types optionnels. Je pense qu'il s'agit d'un excellent article, rédigé de manière claire.

Mise à jour 3 :

Un autre article utile sur le facultatif implicitement déballé utiliser pour le point d'exclamation : " Swift et le dernier kilomètre "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 ses frameworks Objective-C qui peuvent contenir nil. Déclarer un type comme optionnel (en utilisant ? ) ou implicitement déballée (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 implicitement unwrapped, ce qui rend le code d'appel plus pratique, mais moins sûr.

Peut-être qu'Apple pourrait passer au peigne fin ses frameworks à l'avenir, en supprimant l'incertitude des paramètres implicitement déballés ("probablement jamais nil") et en les remplaçant par des déclarations optionnelles ("certainement pourrait être nil dans des circonstances particulières [espérons-le, documentées !]") ou standard non-optionnelles ("n'est jamais nil"), sur la base du comportement exact de leur code Objective-C.

0 votes

Je ne suis pas sûr de cette explication. Si vous exécutez le code sans le !, il renvoie toujours la valeur réelle. Peut-être que le ! est pour la rapidité ?

0 votes

OK. La documentation parle donc d'utiliser ! quand on est sûr qu'il peut être déballé. Mais vous pouvez exécuter le code sans l'utiliser (une quatrième option pour votre liste - le déballage implicite) ET sans vérification préalable. Vous récupérez la valeur ou nil si nil. Mais si vous êtes sûr que ce n'est pas nil, utilisez !......mais je ne vois toujours pas pourquoi vous feriez cela ?

2 votes

Bonjour @RichardWashington - J'ai ajouté une mise à jour à ma réponse qui, je l'espère, clarifie certains points.

130voto

Amr Points 91

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
}

Alors que

john!.apartment = number73

Le compilateur interprétera cette ligne comme simplement :

john.apartment = number73

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

Donc, "envelopper" ne signifie pas qu'il s'agit d'une enveloppe de mémoire, mais qu'il s'agit d'une enveloppe de code. Dans ce cas, il s'agit d'une enveloppe avec une instruction "if", et comme Apple accorde une grande attention aux performances d'exécution, elle veut vous donner un moyen de faire fonctionner votre application avec les meilleures performances possibles.

Mise à jour :

Je reviens à cette réponse après 4 ans, car elle m'a valu les plus grandes réputations sur Stackoverflow :) J'ai un peu mal compris la signification du déballage à l'époque. Maintenant, après 4 ans, je crois que le sens du déballage est d'étendre le code à partir de sa forme compacte originale. Il s'agit également de supprimer le flou autour de cet objet, car nous ne sommes pas sûrs par définition qu'il soit nil ou non. Tout comme le réponse d'Ashley ci-dessus pensez-y comme à un cadeau qui ne pourrait rien contenir. Mais je pense toujours que le déballage est un déballage de code et non un déballage basé sur la mémoire comme l'utilisation d'un enum.

9 votes

Dans mon terrain de jeu, john.apartment = numéro73 ne compile pas, vous devez spécifier john ?.apartment = numéro73

2 votes

@ChuckPinkert a raison, la 4ème ligne devrait être modifiée en john ?.apartment = numéro73, mais la réponse est bonne !

0 votes

John.apartment = number73 donne une erreur : value of Optional type 'Person?' not unwrapped : avez-vous voulu utiliser '!' ou '?' ?

70voto

AlexanderN Points 5805

TL;DR

Que signifie un point d'exclamation dans le langage Swift ?

Le point d'exclamation dit effectivement "Je sais que cette option a définitivement une valeur ; veuillez l'utiliser". C'est ce qu'on appelle le 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."

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

11 votes

Votre réponse est géniale parce que je comprends ce que est en cours maintenant. Ce que je ne comprends pas, c'est la raison d'être des optionnels implicitement déballés. Pourquoi créer quelque chose de défini comme une chaîne optionnelle implicitement déballée, plutôt qu'un type de chaîne ordinaire ? Leur utilisation par la suite est la même. Qu'est-ce que j'ai raté ?

0 votes

Cela ne semble plus être vrai. Dans un Swift 5 repl, si je fais let f: String! = "hello" et ensuite print(f) la sortie est Optional("hello") au lieu de seulement "hello" .

37voto

Ben Gottlieb Points 59900

Si john était une var optionnelle (déclarée ainsi)

var john: Person?

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

Le point d'exclamation dit essentiellement au compilateur "Je sais que ceci a une valeur, vous n'avez pas besoin de le tester". Si vous ne voulez pas l'utiliser, vous pouvez le tester de manière conditionnelle :

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

L'intérieur de celui-ci ne sera évalué que si john a une valeur.

4 votes

Merci pour la réponse, je comprends maintenant ce que le point d'exclamation dit, j'essaie toujours d'envelopper ma tête autour pourquoi ... Vous avez dit que cela dit au compilateur "Je sais que ceci a une valeur, vous n'avez pas besoin de la tester". Qu'est-ce que cela m'apporte si le compilateur ne le teste pas ? Sans le point d'exclamation, le compilateur va-t-il lancer une erreur (je n'ai pas encore d'environnement où je peux tester Swift) ? Est-ce que le ! 100% nécessaire pour toutes les variables optionnelles ? Si c'est le cas, pourquoi Apple s'est-il donné la peine de le faire, au lieu de simplement faire en sorte que les variables de type absence de de ! signifie "Je sais que ceci a une valeur, vous n'avez pas besoin de le tester" ?

0 votes

"Le compilateur va-t-il lancer une erreur ? Non, il fonctionne toujours bien et renvoie la valeur comme prévu. Je ne comprends pas. Est-ce que le ! est juste pour la vitesse quand vous êtes sûr peut-être ?

0 votes

En fait, plus loin dans la documentation, on parle d'optionnels implicitement déballés en utilisant l'exemple ci-dessous : “let possibleString: String? = "An optional string." println(possibleString!) // requires an exclamation mark to access its value // prints "An optional string.” Mais il fonctionne bien sans ! Il y a quelque chose de bizarre ici.

24voto

Fry Points 3149

john est un élément facultatif var et il peut contenir un nil valeur. Pour s'assurer que la valeur n'est pas nulle, utilisez une balise ! à la fin de la var nom.

De la documentation

"Une fois que vous êtes sûr que l'option contient bien 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 définitivement une valeur ; veuillez l'utiliser".

Une autre façon de vérifier une valeur non nulle est (unwrapping optionnel)

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

1 votes

Oui, j'avais vu cela dans la documentation, mais cela me semble toujours inutile... comme, pour moi, john.apartment = number73 dit aussi "Je sais que cette option a définitivement une valeur ; veuillez l'utiliser"...

7 votes

Oui, mais le fait est que Swift essaie par défaut de détecter les erreurs au moment de la compilation. Si vous savez ou pensez savoir que cette variable ne peut pas contenir de nil, vous pouvez supprimer cette vérification en utilisant le point d'exclamation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X