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


518

Le guide du langage de programmation Swift 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éballer 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 de comprendre les bases.


MISE À JOUR:
Le gros morceau du puzzle qui me manquait (pas directement indiqué dans les réponses - du moins pas au moment de la rédaction de ceci) est que lorsque vous effectuez les opérations suivantes:

var john: Person?

cela ne signifie PAS que " johnest de type Personet qu'il pourrait être nul", comme je le pensais à l'origine. J'avais simplement mal compris cela Personet ce Person?sont des types complètement différents. Une fois que j'ai compris cela, tous les autres ?, la !folie et les grandes réponses ci-dessous, avaient beaucoup plus de sens.

Réponses:


530

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

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

Le terme «enveloppé» implique que nous devrions considérer une variable facultative comme un cadeau, enveloppé dans du papier brillant, qui pourrait (malheureusement!) Être vide .

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

S'il existe une valeur, celle-ci peut être obtenue en "dépliant" la variable (en obtenant la Tde Some(T)).

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

Si vous écrivez le nom d'une variable facultative (par exemple, du texte john, sans le !), cela fait référence à l'énumération "enveloppée" (Some / None), pas à la valeur elle-même (T). Ce johnn'est donc pas une instance de Person, et il n'a pas de apartmentmembre:

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

La Personvaleur réelle peut être dépliée de différentes manières:

  • "dépliage forcé": john!(donne la Personvaleur si elle existe, erreur d'exécution si elle est nulle)
  • "liaison facultative": if let p = john { println(p) }(exécute le printlnsi la valeur existe)
  • "chaînage optionnel": john?.learnAboutSwift()(exécute cette méthode reconstitué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 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 gérer le cas nul).

Mise à jour :

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

Jusqu'à présent, dans les exemples, la johnvariable a été déclarée comme var john:Person?, et il s'agit d'un facultatif. 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 était déclarée comme à la var john:Person!place, la variable serait une option implicitement non enveloppée (voir la section avec cette rubrique 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 implicitement non enveloppées ne doivent pas être utilisées lorsqu'il existe une possibilité qu'une variable devienne nulle à un stade ultérieur. 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 « Fonctionnalités Swift intéressantes » de Mike Ash donne une certaine motivation pour les types facultatifs. Je pense que c'est une excellente écriture claire.

Mise à jour 3 :

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

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


Je ne suis pas sûr de cette explication. Si vous exécutez simplement le code sans! il renvoie toujours la valeur réelle. Peut-être le ! c'est pour la vitesse?
Richard Washington

D'ACCORD. Les docs parlent donc de l'utilisation! quand vous savez avec certitude qu'il peut être déballé. Mais vous pouvez exécuter le code correctement sans lui (une quatrième option pour votre liste - déballage implicite) ET sans vérifier d'abord. Vous récupérez la valeur ou nul si nul. Mais si vous savez avec certitude que ce n'est pas nul alors utilisez! ...... mais je ne vois toujours pas pourquoi vous feriez cela?
Richard Washington

2
Salut @RichardWashington - J'ai ajouté une mise à jour à ma réponse qui, je l'espère, clarifie certains de ces éléments.
Ashley

La mise à jour 3 est exactement ce que je recherche. Avant de le lire, je pensais qu'Apple mentait quand ils ont renvoyé quelque chose comme NSURLCredential! qui pourrait en fait être nul.
Golden Thumb

Merci pour la réponse fantastique @Ashley. Après avoir lu cette réponse et certains exemples de code rapide d'Apple, il me semble vraiment que dans ce compromis entre sécurité et productivité, il est généralement préférable d'être du côté le plus sûr. Une exception notable que j'ai trouvée est quand Apple utilise le déballage forcé pour les éléments d'interface utilisateur, ce qui est logique parce que d'abord, les éléments d'interface utilisateur sont généralement facultatifs lorsqu'ils impliquent un storyboard (car ils ne sont pas assignés une valeur par programme), et deuxièmement parce qu'ils sont presque jamais nul sauf si leur contrôleur de vue contenant est nul, auquel cas cette discussion est théorique.
Mihir

126

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! déballera l'instruction if et accélérera son exécution, mais si john est nul, une erreur d'exécution se produira.

Donc, boucler ici ne signifie pas qu'il est encapsulé en mémoire, mais cela signifie qu'il est encapsulé dans du code, dans ce cas, il est encapsulé avec une instruction if, et parce qu'Apple prête une attention particulière aux performances lors de l'exécution, ils veulent vous donner un moyen de faites fonctionner votre application avec les meilleures performances possibles.

Mise à jour:

Pour en revenir à cette réponse après 4 ans, car j'ai obtenu la réputation la plus élevée dans Stackoverflow :) J'ai mal compris un peu le sens du déballage à ce moment-là. Maintenant, après 4 ans, je crois que le sens du déballage ici est d'étendre le code à partir de sa forme compacte d'origine. Cela signifie également supprimer le flou autour de cet objet, car nous ne sommes pas sûrs par définition qu'il soit nul ou non. Tout comme la 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ébouchage basé sur la mémoire comme utilisant l'énumération.


9
Dans mon terrain de jeu, john.apartment = number73 ne se compile pas, vous devez spécifier john? .Apartment = number73
Chuck Pinkert

2
@ChuckPinkert a raison, la 4ème ligne devrait être modifiée en john? .Apartment = number73, belle réponse cependant!
Bruce

john.apartment = number73 donne l'erreur: valeur de type facultatif 'Person?' pas déballé: vouliez-vous utiliser "!" ou '?'?
spiderman

4
Le déballage n'a rien à voir avec les performances. La «vérification nulle» doit toujours être effectuée au moment de l'exécution, la seule différence étant qu'une erreur d'exécution sera levée si aucune valeur n'est présente dans le Facultatif.
madidier

67

TL; DR

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

Le point d'exclamation indique effectivement: «Je sais que cette option a définitivement une valeur; veuillez l'utiliser. " 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."

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


11
Votre réponse est excellente car je comprends ce qui se passe actuellement. Ce que je ne comprends pas, c'est pourquoi les options implicitement déballées existent. Pourquoi créer quelque chose défini comme une chaîne optionnelle implicitement non enveloppée, plutôt que comme un type de chaîne normal? Leur utilisation par la suite est la même. Qu'est-ce que je rate?
pachun

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

37

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

var john: Person?

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

Le point d'exclamation indique essentiellement au compilateur "Je sais que cela a une valeur, vous n'avez pas besoin de la tester". Si vous ne vouliez 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.


4
Merci pour la réponse, je comprends maintenant ce que dit le point d'exclamation, j'essaie toujours de faire le tour de ma tête comprendre pourquoi ... Vous avez dit qu'il dit au compilateur "Je sais que cela a une valeur, vous n'avez pas besoin de tester il". Qu'est-ce que cela m'achète lorsque 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 le ! 100% nécessaire pour tous les vars en option? Si c'est le cas, pourquoi Apple s'en est-il donné la peine, au lieu de simplement en faire le manque ! signifie "Je sais que cela a une valeur, vous n'avez pas besoin de le tester"?
Troy

"le compilateur va-t-il lancer une erreur" - non cela fonctionne toujours très bien en retournant la valeur comme prévu. Je ne comprends pas. Est le ! juste pour la vitesse quand tu es sûr peut-être?
Richard Washington

En fait, plus tard, les documents parlent d'options optionnelles implicitement déballées 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 cela fonctionne très bien sans! Quelque chose semble étrange ici.
Richard Washington

3
@RichardWashington Vous êtes confus pour une bonne raison! Les exemples dans les documents laissent certains éléments de côté et sont trompeurs car ils ne font qu'utiliser println. Apparemment, il existe au moins quelques cas où le déballage des options n'est pas nécessaire. L'une est lors de l'utilisation de println. Une autre consiste à utiliser l'interpolation de chaînes. Alors, peut-être que println et l'interpolation de chaînes se déroulent sous les couvertures? Peut-être que quelqu'un a plus de connaissances à ce sujet. Regarder les définitions d'en-tête Xcode6 Beta ne m'a rien révélé de cette magie.
mbeaty

Oui, je comprends cela John?et je suis de Johndeux types différents. L'un est de type Personne facultative et l'autre Type de personne. La personne facultative doit être déballée avant de pouvoir sortir la personne. Mais comme vous le dites, cela semble se produire au moins dans ces circonstances sans avoir à faire quoi que ce soit. Cela semble faire le! redondant. À moins que le ! est TOUJOURS facultatif mais une chose suggérée à faire pour détecter les erreurs de compilation. Un peu comme attribuer des variables / permet à un type particulier peut être explicite let John: Person = ...mais peut également être déduit let John = ....
Richard Washington

27

Une perspective d'ensemble à ajouter aux autres réponses utiles mais plus centrées sur les détails:

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

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

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

1. Les points d'exclamation contournent 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 vous pensez qu'une erreur pourrait se produire ici, mais je sais avec une certitude absolue que cela ne se produira jamais."

Tout le code valide ne rentre pas dans la boîte du système de type de compilation de Swift - ou dans la vérification de type statique d' une langue, d'ailleurs. 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 excluez d'avoir un chemin de récupération pour une erreur, ce qui signifie que…

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

Un point d'exclamation dit également: "Hé Swift, je suis si certain que cette erreur ne peut jamais se produire qu'il vaut mieux que vous plantiez toute mon application que pour moi de coder un chemin de récupération pour elle."

C'est une affirmation dangereuse. Il peut être le bon: dans le code critique où vous avez bien réfléchi aux invariants de votre code, il se peut que la sortie fausse soit pire qu'un crash.

Cependant, quand je vois !à l'état sauvage, il est rarement utilisé avec autant de conscience. Au lieu de cela, cela signifie trop souvent: «cette valeur était facultative et je n'ai pas vraiment réfléchi à la raison pour laquelle elle pourrait être nulle ou à la façon de gérer correctement cette situation, mais en ajoutant! fait compiler ... donc mon code est correct, non?"

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

3. Les points d'exclamation sont mieux utilisés avec parcimonie.

Chacune de ces !constructions a une ?contrepartie qui vous oblige à faire face au cas d'erreur / nul:

  • Déballage conditionnel: if let name = nameLabel?.text { ... }
  • Optionnels: var logo: UIImageView?
  • Moulages conditionnels: logo.image = thing as? UIImage
  • Exceptions de zéro en cas d'échec: try? NSJSONSerialization.JSONObjectWithData(data, [])

Si vous êtes tenté d'utiliser !, il est toujours bon de considérer attentivement pourquoi vous n'utilisez pas à la ?place. Est-ce que planter votre programme est vraiment la meilleure option si l' !opération échoue? Pourquoi cette valeur est-elle facultative / disponible?

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

Si cela ne peut pas être nul, si l'erreur ne peut jamais se produire, alors existe-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 aucun moyen raisonnable de gérer une erreur, et simplement ignorer l'erreur - et donc procéder avec des données erronées - serait pire que de planter. Ceux le moment d'utiliser le déballage forcé.

Je recherche périodiquement l'ensemble de ma base de code !et vérifie chaque utilisation de celui-ci. Très peu d'utilisations résistent à l'examen. (Au moment de la rédaction de ce document, l'ensemble du framework Siesta en avait exactement deux instances .)

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


func isSubscriptionActive(receiptData: NSDictionary?) -> Bool { if(receiptData == nil) { return false; } return (hasValidTrial(receiptData!) || isNotExpired(receiptData!)) && isNotCancelled(receiptData!) } Étant donné 3. existe-t-il une meilleure façon de l'écrire?
jenson-button-event

Vous pouvez direfunc isSubscriptionActive(receiptData: NSDictionary?) -> Bool { guard let nonNilReceiptData = receiptData else { return false} return (hasValidTrial(nonNilReceiptData) || isNotExpired(nonNilReceiptData)) && isNotCancelled(nonNilReceiptData) }
rkgibson2

Les points d'exclamation ne contournent aucun contrôle de sécurité. Vous obtenez un plantage garanti si l'option est nulle. L'option est toujours cochée. C'est juste une façon très brutale de faire un contrôle de sécurité.
gnasher729

@ gnasher729 Comme l'indique la réponse, il contourne les contrôles de sécurité au moment de la compilation en faveur d'une défaillance de l' exécution en toute sécurité .
Paul Cantrell

24

johnest une option var. Peut donc contenir une nilvaleur. Pour vous assurer que la valeur n'est pas nulle, utilisez un !à la fin du varnom.

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 indique effectivement: «Je sais que cette option a définitivement une valeur; veuillez l'utiliser. "

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

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

1
Oui, je l'avais vu dans la documentation, mais cela semble toujours inutile ... comme, pour moi, john.apartment = number73dit également "Je sais que cette option a certainement une valeur; veuillez l'utiliser." ...
Troy

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

16

Voici quelques exemples:

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

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

word = name 

Voici nameune valeur pour que nous puissions l'attribuer

var cow:String = nil
var dog:String!

dogest déplié avec force signifie qu'il doit contenir une valeur

dog = cow

L'application se bloquera car nous sommes assignés nilà des dépliés


1
var c:Int = nilobtiendra: "Nil ne peut pas initialiser le type spécifié 'int'"
Asususer

15

Dans ce cas...

var John: Personne!

cela signifie qu'au départ, John aura une valeur nulle, elle sera définie et une fois définie, elle ne sera plus jamais dirigée par zéro. 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 implicitement non enveloppée»


1
Je vous remercie. J'avais également trouvé le lien suivant qui explique cela. Ce type de type est appelé une "option implicitement déballée". stackoverflow.com/questions/24122601/…
Kaydell

5

Si vous venez d'un langage de la famille C, vous penserez "pointeur vers un objet de type X qui pourrait être l'adresse mémoire 0 (NULL)", et si vous venez d'un langage typé dynamiquement vous serez penser "Objet qui est probablement de type X mais qui pourrait être de type non défini". Aucun de ces éléments n'est en fait correct, bien que d'une manière détournée, le premier soit proche.

La façon dont vous devriez y penser est 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 revient vraiment foo.isNil, et lorsque vous dites foo!qu'elle revient foo.realObjectavec une affirmation foo.isNil == false. Il est important de le noter car si en foofait il est nulfoo! , c'est une erreur d'exécution, donc vous voudrez généralement utiliser un let conditionnel à la place, sauf si vous êtes très sûr que la valeur ne sera pas nulle. Ce genre de ruse signifie que la langue peut être fortement typée sans vous forcer à tester si les valeurs sont nulles partout.

En pratique, cela ne se comporte pas vraiment comme ça car le travail est fait par le compilateur. À un niveau élevé, il existe un type Foo?distinct de Foocelui qui empêche les fonctions acceptant le typeFoo de recevoir une valeur nulle, mais à un niveau bas, une valeur facultative n'est pas un véritable objet car elle n'a pas de propriétés ou de méthodes; il est probable qu'en fait c'est un pointeur qui peut être NULL (0) avec le test approprié lors du déballage forcé.

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

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

Cela équivaut à peu près à accepter une option avec un déballage forcé, c'est-à-dire:

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

Vous pouvez l'utiliser pour avoir une méthode qui accepte techniquement une valeur facultative mais qui 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 à la place. Ce n'est généralement pas une bonne idée, mais cela peut être utile lors de la conversion de code à partir d'une autre langue.



3

Si vous connaissez C #, cela ressemble à des 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

2

Dans l'objectif C, les variables sans valeur étaient égales à «nil» (il était également possible d'utiliser des valeurs «nil» identiques à 0 et fausses), d'où la possibilité d'utiliser des variables dans les instructions conditionnelles (les variables ayant des valeurs sont identiques à «TRUE» »et ceux sans valeur étaient égaux à« FAUX »).

Swift assure la sécurité du type en fournissant une «valeur facultative». c'est-à-dire qu'il empêche les erreurs formées d'affecter des variables de différents types.

Ainsi, dans Swift, seuls les 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 l'objectif 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

Le ! à la fin d'un objet dit que l'objet est facultatif et à déballer s'il peut sinon retourner un néant. Ceci est souvent utilisé pour intercepter les erreurs qui pourraient autrement planter le programme.


2

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

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

sinon, vous devriez le faire à chaque fois que vous avez passé une valeur ...

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

1

John est une personne facultative, ce qui signifie qu'elle peut contenir une valeur ou être nulle.

john.apartment = number73

est utilisé si john n'est pas facultatif. Puisque john n'est jamais nul, nous pouvons être sûrs qu'il n'appellera pas apartment sur une valeur nulle. Tandis que

john!.apartment = number73

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

La documentation comprend un bel exemple d'utilisation de ceci où convertNumber est facultatif.

if convertedNumber {
    println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}

Êtes-vous en train de dire que si john est nul, et je pars john.apartment = number73, cela ne donnera pas lieu à une erreur à moins que je mette un '!' après john?
Troy

Si john est facultatif, vous devez utiliser le point d'exclamation pour accéder à ses propriétés car le compilateur doit savoir qu'il n'est pas nul. Si ce n'est pas facultatif, vous ne pouvez pas utiliser le point d'exclamation.
Connor

1
Si je dois utiliser un point d'exclamation pour TOUS les fichiers optionnels, alors pourquoi Apple s'en est-il même donné la peine, au lieu de le faire de sorte que l' absence d'un point d'exclamation signifie la même chose? Cela semble être un ballonnement inutile ... Ou y a-t-il un cas où il est logique de NE PAS utiliser un point d'exclamation avec une var facultative?
Troy

vous utilisez le point d'exclamation lorsque vous avez besoin que l'option ne soit pas nulle. Comme dans le cas de l'accès aux propriétés de John. vous pourriez faire quelque chose comme var jack: Personne? = john où jack est également un jack optionnel ou var: Person = john! où jack est une personne et n'est pas nul
Connor

D'accord, mais, dans l'exemple d'origine, le fait que je déréférencer l' johnoption ne dise pas au compilateur que j'ai besoin qu'il soit non nul? ... Et dans votre var jack: Person = john!exemple, le manque d'un? après que personne ait dit au compilateur que vous devez johnêtre non nul? Dans l'ensemble, le !semble semble un peu redondant ... J'ai toujours l'impression que je manque quelque chose sur la partie "pourquoi" de !...
Troy

1

Pour le dire simplement, les points d'exclamation signifient qu'une option 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 forcer le déballage. Si vous forcez le déballage d'une option qui est vide, votre programme se bloquera, alors soyez prudent! 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 aucune valeur. Si je devais le déballer, le programme planterait et Xcode vous dirait que vous avez essayé de déballer une option avec une valeur nulle.

J'espère que cela a aidé.


1

EN MOTS SIMPLES

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


1

L'histoire entière commence par une fonctionnalité de vars rapides appelés optionnels. Ce sont les vars qui peuvent avoir une valeur ou non. 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 et également servir un espace réservé pour les backdoors. Ainsi, pour déclarer une variable dont la valeur n'est pas initialement déterminée, nous utilisons un «?». Lorsqu'une telle variable est déclarée, pour l'utiliser comme partie d'une expression, il faut les déballer avant de l'utiliser, le déballage est une opération par laquelle la valeur d'une variable est découverte, cela s'applique aux objets. Sans déballer si vous essayez de les utiliser, vous aurez une erreur de temps de compilation. Pour déballer une variable qui est une variable facultative, point d'exclamation "!" est utilisé.

Il y a maintenant des moments où vous savez que de telles variables facultatives se verront attribuer des valeurs par le système par exemple ou votre propre programme, mais un peu plus tard, par exemple les sorties d'interface utilisateur, dans une telle situation 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 facultatif en ce moment et n'a aucune valeur mais recevra une valeur plus tard dans sa vie.

Ainsi, le point d'exclamation possède deux usages différents, 1. Déclarer une variable qui sera facultative et recevra définitivement la valeur 2. Développer une variable facultative avant de l'utiliser dans une expression.

Les descriptions ci-dessus évitent trop de choses techniques, j'espère.


1

Si vous l'utilisez comme optionnel, il déballe l'option et voit s'il y a quelque chose. Si vous l'utilisez dans une instruction if-else, c'est le code pour NOT. Par exemple,

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

1

Une variable facultative peut contenir une valeur ou ne pas l'être

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

cas 2: var myVar:String? = nil

maintenant, si vous demandez à myVar !, vous dites au compilateur de renvoyer une valeur au cas où 1 reviendrait "Something"

dans le cas 2, il plantera.

Sens ! La marque forcera le compilateur à renvoyer une valeur, même si elle n'est pas là. c'est pourquoi le nom Force Unwrapping .


@Moritz. Je ne sais pas, c'est ça? Je ne peux pas répondre si une question a déjà reçu une réponse? Suis-je à l'origine d'un problème ou y a-t-il un problème dans ma réponse? je ne comprends pas pourquoi votez-vous en bas pour y répondre correctement? J'ai essayé de répondre en fonction de ma compréhension du concept. je pense que certaines personnes le trouveront utile pour une explication claire et facile.
Ashish Pisey

@Moritz Maintenant, c'est constructif. Vous auriez dû me dire cette correction en premier lieu, si c'était la raison. Merci pour les commentaires. je l'ai corrigé.
Ashish Pisey

0
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

1
Veuillez ne pas écrire de réponses qui n'ajoutent rien qui n'a pas déjà été couvert par les réponses précédentes.
Dalija Prasnikar

-1

DEMANDE TOI

  • Le type person?a- t- il un apartmentmembre / une propriété? OU
  • Le type persona- t- il un apartmentmembre / une propriété?

Si vous ne pouvez pas répondre à cette question, continuez à lire:

Pour comprendre, vous devrez peut-être un niveau super-basique de compréhension des génériques . Voyez ici . Beaucoup de choses dans Swift sont écrites en utilisant des génériques. Optionnels inclus

Le code ci-dessous est disponible à partir de cette vidéo de Stanford . Je vous recommande vivement de regarder les 5 premières minutes

Une option est une énumération avec seulement 2 cas

enum Optional<T>{
    case None
    case Some(T)
}

let x: String? = nil //actually means:

let x = Optional<String>.None

let x :String? = "hello" //actually means:

let x = Optional<String>.Some("hello")

var y = x! // actually means:

switch x {
case .Some(let value): y = value
case .None: // Raise an exception
}

Reliure optionnelle:

let x:String? = something
if let y = x {
    // do something with y
}
//Actually means:

switch x{
case .Some(let y): print)(y) // or whatever else you like using 
case .None: break
}

quand vous dites que vous voulez dire var john: Person?par exemple:

enum Optional<Person>{
case .None
case .Some(Person)
}

L'énumération ci-dessus a-t-elle une propriété nommée apartment? Le voyez-vous quelque part? Ce n'est pas là du tout! Cependant, si vous le déballez, c'est-à-dire, person!vous pouvez ... ce qu'il fait sous le capot est:Optional<Person>.Some(Person(name: "John Appleseed"))


Si vous aviez défini var john: Personau lieu de: var john: Person?alors vous n’auriez plus eu besoin de l’ !utiliser, car Personlui-même a un membre deapartment


Comme une discussion future sur les raisons pour lesquelles l'utilisation !de déballer n'est parfois pas recommandée, voir ce Q&R


2
L'utilisation des diapositives ci-dessus est peut-être une violation du droit d'auteur: "... l'Université de Stanford conserve le droit d'auteur sur tout le contenu de notre collection iTunes U." , sur http://itunes.stanford.edu . Il vaut probablement mieux répondre, dans vos propres mots, au contenu que vous avez appris du cours que vous percevez pour répondre à cette question (et au lieu d'utiliser les diapositives, citez plutôt les parties pertinentes sous forme de code, avec des références, naturellement).
dfri

2
Votre argument est argumentum ad populum . Quoi qu'il en soit, je ne pense pas que Stanford viendra ici et appliquera les droits DMCA , mais c'est toujours mieux si vos réponses sont dans vos propres mots basées sur des sources officielles / valides , et non sous la forme de copier ces sources de manière pure comme ci-dessus; surtout quand on parle de diapositives protégées par le droit d'auteur: encore une fois, mieux vaut citer le contenu des diapositives dans des blocs de code (les images de code sont généralement un non-non ici à SO!).
dfri

1
La méta-publication à laquelle j'ai lié décrit simplement l'approche de SO: s à ce sujet: une publication comme celle-ci ne sera naturellement pas supprimée si un utilisateur aléatoire la signale comme une violation du droit d'auteur: uniquement si, par exemple, un représentant de Stanford. devaient venir ici et que le poste à supprimer aurait SO besoin de l' appliquer. C'est pourquoi j'ai écrit " Je ne crois pas que Stanford viendra ici et fera valoir les droits DMCA, ..." . Mon point ci-dessus est que je pense que cela peut être une violation du droit d'auteur (ce que je pense être faux), mais naturellement personne ne l'appliquera jamais. ...
dfri

1
Mais à part cela, la publication de questions ou de réponses contenant des images de code est déconseillée pour un certain nombre de raisons. Pour résumer: j'ai essayé de souligner avec ces commentaires que je pense que cette réponse, dans sa forme actuelle, n'est pas aussi bonne qu'elle pourrait l'être, pour deux raisons principales (diapositives protégées par le droit d'auteur, image de code). Naturellement, personne, y compris moi-même, ne fera rien à ce sujet, je le pointe simplement vers notre future référence (ou je vous encourage peut-être à modifier ce message avec des citations par blocs de code à la place). D'accord, iTunes U! :)
dfri

2
@dfri a supprimé les images
Honey
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.