web-dev-qa-db-fra.com

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

Le Swift Guide du langage de programmation présente 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, lors de l'attribution de l'appartement à la personne, ils utilisent un point d'exclamation pour "décompresser l'instance":

john!.apartment = number73

Que signifie "décompresser 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 de comprendre les bases.


UPDATE:
Le gros morceau du puzzle qui me manquait (et non pas indiqué directement dans les réponses - du moins pas au moment d'écrire ceci) est que lorsque vous procédez comme suit:

var john: Person?

cela ne signifie PAS que "john est de type Person et il pourrait être nul", comme je le pensais à l'origine. Je ne comprenais tout simplement pas que Person et Person? sont des types complètement séparés. Une fois, j’ai compris que toutes les autres ?, ! et les bonnes réponses ci-dessous avaient beaucoup plus de sens.

511
Troy

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

Autant que je peux m'entraîner (c'est très nouveau pour moi aussi) ...

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

Lorsqu'elle est "encapsulée", la valeur d'une variable optionnelle est une énumération avec deux valeurs possibles (un peu comme un booléen). Cette énumération indique si la variable contient une valeur (Some(T)) ou non (None).

S'il existe une valeur, vous pouvez l'obtenir en "dépliant" la variable (en obtenant la T de Some(T)).

En quoi john!.apartment = number73 est-il différent de john.apartment = number73? (Paraphrasé)

Si vous écrivez le nom d'une variable optionnelle (par exemple text john, sans le !), cela fait référence à l'énumération "wrapped" (Some/None), pas à la valeur elle-même (T). Donc, john n'est pas une instance de Person, et n'a pas de membre apartment:

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

La valeur réelle de Person peut être décomposée de différentes manières:

  • "décompression forcée": john! (donne la valeur Person si elle existe, erreur d'exécution si elle est nulle)
  • "binding optionnelle": if let p = john { println(p) } (exécute la println si la valeur existe)
  • "chaining optionnel": john?.learnAboutSwift() (exécute cette méthode inventée si la valeur existe)

J'imagine que vous choisissez l'une de ces méthodes pour décompresser, en fonction de ce qui devrait se passer dans le cas zéro et de sa probabilité. Cette conception de langage oblige le cas nul à ê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 nul).

Mise à jour :

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

Dans les exemples précédents, la variable john a été déclarée comme var john:Person? et il s’agit d’un Facultatif. Si vous voulez connaître la valeur réelle de cette variable, vous devez la décompresser en utilisant l’une des trois méthodes ci-dessus.

Si elle était plutôt déclarée comme var john:Person!, la variable serait une option implicitement non enveloppée (voir la section contenant cet en-tête dans le manuel d'Apple). Il n'est pas nécessaire de décompresser ce type de variable lors de l'accès à la valeur, et john peut être utilisé sans syntaxe supplémentaire. Mais le livre d'Apple dit:

Les options non implicites non enveloppées ne doivent pas être utilisées lorsqu'il est possible qu'une variable devienne nulle par la suite. Utilisez toujours un type facultatif normal si vous devez rechercher une valeur nulle pendant la durée de vie d'une variable.

Mise à jour 2 :

L'article " Intéressant Swift Caractéristiques " de Mike Ash donne une certaine motivation pour les types optionnels. Je pense que c'est bien, une écriture claire.

Mise à jour 3 :

Un autre article utile sur l'utilisation de implicitement implicite pour le point d'exclamation: " Swift and the Last Mile " de Chris Adamson. L'article explique qu'il s'agit d'une mesure pragmatique de Apple utilisée pour déclarer les types utilisés par leurs frameworks Objective-C susceptibles de contenir nil. Déclarer un type comme étant facultatif (avec ?) ou implicitement non encapsulé (avec !) est un "compromis entre sécurité et commodité". Dans les exemples donnés dans l'article, Apple ont choisi de déclarer les types implicitement non enveloppés, ce qui rend le code d'appel plus pratique, mais moins sûr.

Peut-être Apple pourrait-il explorer leurs cadres à l'avenir, en supprimant l'incertitude des paramètres implicitement non enveloppés ("probablement jamais nul") et en les remplaçant par des éléments optionnels ("pourrait certainement être nul en particulier [espérons-le, documenté!] circonstances ") ou des déclarations standard non facultatives (" n'est jamais jamais "), basées sur le comportement exact de leur code Objective-C.

519
Ashley

Voici ce que je pense est la différence:

var john: Person?

Signifie que john peut être nul

john?.apartment = number73

Le compilateur interprétera cette ligne comme suit:

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éballer l'instruction if et la faire courir plus vite, mais si john est nil, une erreur d'exécution se produira.

Donc, encapsuler ici ne signifie pas que la mémoire est encapsulée, mais que le code est encapsulé, dans ce cas, il est encapsulé avec une instruction if, et parce que Apple est très attentif aux performances d'exécution, ils veulent pour vous permettre de faire fonctionner votre application avec les meilleures performances possibles.

Mise à jour:

Revenons à cette réponse au bout de 4 ans, alors que Stackoverflow en tirait la plus haute réputation. J'ai un peu mal compris le sens de la décompression à l'époque. Maintenant, après 4 ans, je pense que le sens de la décompression est d’élargir le code à partir de sa forme compacte originale. Cela signifie également que vous devez éliminer le flou autour de cet objet, car nous ne sommes pas sûrs que, par définition, il est nul ou non. Tout comme la réponse d'Ashley ci-dessus, considérez cela comme un cadeau qui ne pourrait rien contenir. Mais je pense toujours que le déchiffrement est un déchiffrement de code et non un décompressage basé sur la mémoire comme enum.

127
Amr

TL; DR

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

Le point d'exclamation indique en effet: "Je sais que cette option a définitivement une valeur; veuillez l’utiliser. "C’est ce que l’on appelle le déroulement 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

65
Alex Nolasco

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

var john: Person?

alors il serait possible pour john de n'avoir aucune valeur (dans le jargon ObjC, valeur nulle)

Le point d'exclamation dit 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 de ceci n'évaluera que si john a une valeur.

37
Ben Gottlieb

Quelques grandes perspectives à 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
  • Options en option implicitement non emballées: var logo: UIImageView!
  • Coulée forcée: logo.image = thing as! UIImage
  • Exceptions non gérées: try! NSJSONSerialization.JSONObjectWithData(data, [])

Chacun de ceux-ci est une construction de langage différente avec un sens différent, mais ils ont tous trois points communs importants:

1. Les points d’exclamation contournent les contrôles de sécurité effectués par Swift au moment de la compilation.

Lorsque vous utilisez ! dans Swift, vous dites essentiellement: "Hé, compilateur, je sais que vous pensez qu'une erreur pourrait se produire ici, mais je connais avec la certitude absolue que cela ne le sera jamais. "

Tout le code valide n’entre pas dans la boîte du système de types au moment de la compilation de Swift - ou la vérification de type statique du langage , en fait. Il existe des situations où vous pouvez prouver logiquement 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 fonctionnalités en premier lieu.

Cependant, chaque fois que vous utilisez !, vous n’excluez pas de 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 également: "Hey Swift, je suis donc certain que cette erreur ne peut jamais arriver, il est préférable que vous ( plantiez mon application entière . que ce soit pour moi de coder un chemin de récupération pour cela. "

C’est une affirmation dangereuse. Cela peut être 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é aussi consciencieusement. Au lieu de cela, cela signifie trop souvent que "cette valeur était facultative et je ne pensais pas vraiment trop pourquoi cela pouvait être nul ou comment gérer correctement cette situation, mais en ajoutant ! l'a fait compiler… donc mon code est correct, n'est-ce pas?

Attention à l'arrogance du point d'exclamation. Au lieu…

3. Il vaut mieux utiliser les points d’exclamation avec modération.

Chacune de ces constructions ! a un équivalent ? qui vous oblige à traiter le cas d'erreur/nil:

  • Déballage conditionnel: if let name = nameLabel?.text { ... }
  • Options: var logo: UIImageView?
  • Incantations conditionnelles: logo.image = thing as? UIImage
  • Exceptions nulles en cas d'échec: try? NSJSONSerialization.JSONObjectWithData(data, [])

Si vous êtes tenté d'utiliser !, il est toujours bon de bien examiner pourquoi vous n'utilisez pas ?. Faire planter votre programme est-il vraiment la meilleure option si l'opération ! échoue? Pourquoi cette valeur est-elle facultative/accessible?

Existe-t-il un chemin de récupération raisonnable que votre code pourrait emprunter en cas d'erreur/d'erreur? Si oui, codez-le.

Si elle ne peut pas être nulle, si l’erreur ne peut jamais se produire, existe-t-il un moyen raisonnable de retravailler votre logique pour que le compilateur le sache? Si oui, fais-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 le fait de simplement ignorer l'erreur - et donc de traiter des données erronées - serait pire que de planter. Ceux sont les moments où il faut utiliser le déballage forcé.

Je recherche périodiquement toute la base de code de ! et en vérifie chaque utilisation. Très peu d'usages résistent à l'examen. (Au moment d'écrire ces lignes, tout le framework Siesta en possède exactement deuxinstances .)

Cela ne veut pas dire que vous ne devriez jamais utiliser ! dans votre code - vous devez simplement l’utiliser avec attention , et ne jamais en faire la option par défaut.

27
Paul Cantrell

john est une option var. Donc, peut contenir une valeur nil. Pour vous assurer que la valeur n'est pas nulle, utilisez un ! à la fin du nom var.

De la documentation

“Une fois que vous êtes certain que l’optif contient bien une valeur, vous pouvez accéder à sa valeur sous-jacente en ajoutant un point d’exclamation (!) À la fin du nom de l’optif. Le point d’exclamation indique en fait:“ Je sais que cette option a définitivement une valeur, merci de l’utiliser. ”

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

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

Voici quelques exemples:

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

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

Word = name 

Ici name a une valeur pour pouvoir l’affecter

var cow:String = nil
var dog:String!

dog est déplié de force signifie qu'il doit contenir une valeur

dog = cow

L'application va planter parce que nous assignons nil à non emballé

16
Ramkumar chintala

Dans ce cas...

var John: Personne!

cela signifie que, initialement, John aura une valeur nulle, il sera défini et une fois défini, il ne sera plus jamais réinitialisé. Par conséquent, pour plus de commodité, je peux utiliser la syntaxe la plus simple pour accéder à une variable facultative, car il s'agit d'une "option facultative implicitement non enveloppée".

15
guest

Si vous venez d'un langage de la famille C, vous penserez "pointeur sur un objet de type X qui pourrait être l'adresse mémoire 0 (NULL)", et si vous venez d'un langage à typage dynamique, vous serez pensant "Objet qui est probablement de type X mais peut être de type non défini". Aucune de ces réponses n'est en réalité correcte, bien que le premier soit proche.

Vous devriez penser à cela comme si c'était un objet comme:

struct Optional<T> {
   var isNil:Boolean
   var realObject:T
}

Lorsque vous testez votre valeur facultative avec foo == nil, elle renvoie réellement foo.isNil et lorsque vous dites foo!, elle renvoie foo.realObject avec une assertion qui foo.isNil == false. Il est important de noter cela car, si foo est réellement nul lorsque vous faites foo!, il s'agit d'une erreur d'exécution. Vous voudrez donc généralement utiliser un let conditionnel, à moins que vous ne soyez vraiment sûr que la valeur ne pas être nul. Ce genre de ruse signifie que le langage peut être fortement typé sans vous obliger à tester si les valeurs sont nulles partout.

En pratique, cela ne se comporte pas vraiment comme ça parce que le travail est fait par le compilateur. A un niveau élevé, il existe un type Foo? séparé de Foo et empêchant les fonctions acceptant le type Foo de recevoir une valeur nulle, mais à un niveau bas une valeur optionnelle isn pas un objet vrai car il n’a pas de propriétés ou de méthodes; il est probable qu’il s’agisse d’un pointeur pouvant avoir la valeur NULL (0) avec le test approprié lors du retrait forcé.

Il existe une autre situation dans laquelle vous verriez qu'un point d'exclamation se trouve sur un type, comme dans:

func foo(bar: String!) {
    print(bar)
}

C’est à peu près équivalent à accepter une option avec un déroulement forcé, c’est-à-dire:

func foo(bar: String?) {
    print(bar!)
}

Vous pouvez utiliser ceci pour avoir une méthode qui accepte techniquement une valeur optionnelle, mais aura une erreur d’exécution si elle est nulle. Dans la version actuelle de Swift, cela contourne apparemment l'assertion is-not-nil, vous aurez donc une erreur de bas niveau. Généralement, ce n'est pas une bonne idée, mais cela peut être utile lors de la conversion de code depuis une autre langue.

4
Jim Driscoll

Si vous êtes familier avec C #, cela ressemble aux types Nullable qui sont également déclarés à l'aide d'un point d'interrogation:

Person? thisPerson;

Et le point d'exclamation dans ce cas équivaut à accéder à la propriété .Value du type nullable comme ceci:

thisPerson.Value
3
Abdurrahman

Le ! signifie que vous forcez à dérouler l'objet le! suit. Vous trouverez plus d'informations dans la documentation d'Apple, à l'adresse suivante: https://developer.Apple.com/library/ios/documentation/Swift/conceptual/Swift_Programming_Language/TheBasics.html

Dans l’objectif C, les variables sans valeur étaient égales à "nil" (il était également possible d’utiliser des valeurs "nil" identiques à 0 et à false), il était donc possible d’utiliser des variables dans des instructions conditionnelles (les variables ayant les mêmes valeurs que "TRUE"). 'et ceux sans valeur étaient égaux à' FALSE ').

Swift assure la sécurité du type en fournissant une "valeur optionnelle". Cela évite les erreurs d’attribution de variables de types différents.

Ainsi, dans Swift, seuls des booléens peuvent être fournis sur des instructions conditionnelles.

var hw = "Hello World"

Ici, même si 'hw' est une chaîne, elle ne peut pas être utilisée dans une instruction if comme dans Objective C.

//This is an error

if hw

 {..}

Pour cela, il doit être créé comme,

var nhw : String? = "Hello World"

//This is correct

if nhw

 {..}
2
Gokul

En bref (!): Une fois que vous avez déclaré une variable et que vous êtes certain que celle-ci contient une valeur.

let assumedString: String! = "Some message..."
let implicitString: String = assumedString

sinon, vous devriez le faire chaque fois que vous passez une valeur ...

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark
2
En Hui Lim

Le ! à la fin d'un objet indique que l'objet est facultatif et à se dérouler s'il peut sinon renvoyer une valeur nulle. Ceci est souvent utilisé pour intercepter des erreurs qui autrement planteraient le programme.

2
cheborneck

Une variable optionnelle peut contenir une valeur ou ne pas être

cas 1: var myVar:String? = "Something"

cas 2: var myVar:String? = nil

maintenant, si vous demandez à myVar !, vous demandez au compilateur de renvoyer une valeur dans le cas 1, il renverra "Something"

dans le cas 2, il va planter.

Sens ! mark obligera le compilateur à renvoyer une valeur, même si elle n’est pas présente. C’est pourquoi le nom Déballage forcé.

1
Ashish Pisey

Si vous l'utilisez en option, il s'ouvre et indique s'il y a quelque chose. Si vous l'utilisez dans une instruction if-else, c'est un code pour NOT. Par exemple,

if (myNumber != 3){
 // if myNumber is NOT 3 do whatever is inside these brackets.
)
1
Andy Lebowitz

Pour le dire simplement, les points d'exclamation signifient qu'un optionnel est en cours de déballage. Une option est une variable qui peut avoir une valeur ou non - vous pouvez donc vérifier si la variable est vide en utilisant une instruction if let comme indiqué ici , puis forcez-la à la dérouler. Si vous forcez le déballage d'un optionnel vide, votre programme va planter, alors faites attention! Les options sont déclarées en mettant un point d'interrogation à la fin d'une affectation explicite à une variable, par exemple, je pourrais écrire:

var optionalExample: String?

Cette variable n'a pas de valeur. Si je devais le déballer, le programme se bloquerait et Xcode vous dirait que vous aviez essayé de déballer une option avec une valeur nulle.

J'espère que ça a aidé.

1
brimstone

IN SIMPLE WORDS

SING Le point d'exclamation indique que la variable doit être constituée d'une valeur non nulle (elle ne sera jamais nulle)

1

Toute l'histoire commence par Swift, une fonctionnalité appelée vars facultatifs. Ce sont les vars qui peuvent avoir une valeur ou ne pas avoir de valeur. En général, Swift ne nous permet pas d'utiliser une variable qui n'est pas initialisée, car cela peut entraîner des plantages ou des raisons inattendues, ainsi que de créer un espace réservé pour les portes dérobées. Ainsi, pour déclarer une variable dont la valeur n’a pas été déterminée initialement, nous utilisons un '?'. Lorsqu'une telle variable est déclarée, pour l'utiliser comme partie d'une expression, il faut la déballer avant son utilisation, le déballage est une opération par laquelle la valeur d'une variable est découverte et s'applique aux objets. Sans décompresser si vous essayez de les utiliser, vous aurez une erreur de compilation. Pour décompresser une variable qui est une variable facultative, le point d'exclamation "!" est utilisé.

Il arrive maintenant que vous sachiez que ces variables facultatives se verront attribuer des valeurs par le système, par exemple, ou ultérieurement par votre propre programme, par exemple les prises d'interface utilisateur, au lieu de déclarer une variable facultative à l'aide d'un point d'interrogation "?" nous utilisons "!".

Ainsi, le système sait que cette variable qui est déclarée avec "!" est optionnel pour le moment et n'a pas de valeur mais recevra une valeur plus tard dans sa vie.

Ainsi, le point d'exclamation a deux utilisations différentes: 1. Déclarer une variable qui sera optionnelle et recevra une valeur définitivement plus tard. 2. Déballer une variable optionnelle avant de l'utiliser dans une expression.

J'espère que les descriptions ci-dessus évitent trop de problèmes techniques.

1
vishal dharankar

John est une personne facultative, ce qui signifie qu'il peut avoir une valeur ou être nul.

john.apartment = number73

est utilisé si john n'est pas une option. Comme John n’est jamais nul, nous pouvons être sûrs qu’il n’appellera pas appartement avec une valeur nulle. Tandis que

john!.apartment = number73

promet au compilateur que john n'est pas nil, puis décompresse l'option permettant d'obtenir la valeur de john et accède à la propriété de john. Utilisez ceci si vous savez que john n'est pas nul. Si vous appelez cela facultativement avec nil, vous obtiendrez une erreur d'exécution.

La documentation inclut un exemple d'utilisation de Nice où convertNumber est facultatif.

if convertedNumber {
    println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}
1
Connor
Simple the Optional variable allows nil to be stored.

var str : String? = nil

str = "Data"

To convert Optional to the Specific DataType, We unwrap the variable using the keyword "!"

func get(message : String){
   return
}

get(message : str!)  // Unwapped to pass as String
0
jeff ayan