web-dev-qa-db-fra.com

Décalage des options à Swift: comme? Tapez ou comme! Type?

Compte tenu de ce qui suit dans Swift:

var optionalString: String?
let dict = NSDictionary()

Quelle est la différence pratique entre les deux déclarations suivantes:

optionalString = dict.objectForKey("SomeKey") as? String

vs

optionalString = dict.objectForKey("SomeKey") as! String?
89
sdduursma

La différence pratique est la suivante:

var optionalString = dict["SomeKey"] as? String

optionalString sera une variable de type String?. Si le type sous-jacent est autre chose qu'un String cela affectera inoffensif simplement nil à l'option.

var optionalString = dict["SomeKey"] as! String?

Cela dit, je sais cette chose est un String?. Cela aussi aura pour résultat que optionalString sera de type String?, mais il plantera si le type sous-jacent est autre chose.

Le premier style est ensuite utilisé avec if let pour décompresser l’option facultative:

if let string = dict["SomeKey"] as? String {
    // If I get here, I know that "SomeKey" is a valid key in the dictionary, I correctly
    // identified the type as String, and the value is now unwrapped and ready to use.  In
    // this case "string" has the type "String".
    print(string)
}
132
vacawama

Pour clarifier ce que vacawama a dit, voici un exemple ...

Swift 3.0:

import UIKit

let str_value:    Any   = String("abc")!
let strOpt_value: Any?  = String("abc")!
let strOpt_nil:   Any?  = (nil as String?)
let int_value:    Any   = Int(1)
let intOpt_value: Any?  = Int(1)
let intOpt_nil:   Any?  = (nil as Int?)

// as String
//str_value     as String // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//strOpt_value  as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//strOpt_nil    as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//int_value     as String // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//intOpt_value  as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//intOpt_nil    as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?

// as? String
  str_value     as? String // == "abc"
  strOpt_value  as? String // == "abc"
  strOpt_nil    as? String // == nil
  int_value     as? String // == nil
  intOpt_value  as? String // == nil
  intOpt_nil    as? String // == nil

// as! String
  str_value     as! String // == "abc"
  strOpt_value  as! String // == "abc"
//strOpt_nil    as! String // Run-Time Error: unexpectedly found nil while unwrapping an Optional value.
//int_value     as! String // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
//intOpt_value  as! String // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
//intOpt_nil    as! String // Run-Time Error: unexpectedly found nil while unwrapping an Optional value.

// as String?
//str_value     as String? // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//strOpt_value  as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//strOpt_nil    as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//int_value     as String? // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//intOpt_value  as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//intOpt_nil    as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?

// as? String?
//str_value     as? String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  strOpt_value  as? String? // == "abc"
  strOpt_nil    as? String? // == nil
//int_value     as? String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  intOpt_value  as? String? // == nil
  intOpt_nil    as? String? // == nil

// as! String?
//str_value     as! String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  strOpt_value  as! String? // == "abc"
  strOpt_nil    as! String? // == nil
//int_value     as! String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
//intOpt_value  as! String? // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
  intOpt_nil    as! String? // == nil

// let _ = ... as String
//if let _ = str_value    as String { true } // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_value as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_nil   as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = int_value    as String { true } // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_value as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_nil   as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?

// let _ = ... as? String
if let _ = str_value    as? String { true } // true
if let _ = strOpt_value as? String { true } // true
if let _ = strOpt_nil   as? String { true } // false
if let _ = int_value    as? String { true } // false
if let _ = intOpt_value as? String { true } // false
if let _ = intOpt_nil   as? String { true } // false

// let _ = ... as! String
//if let _ = str_value    as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = strOpt_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = strOpt_nil   as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = int_value    as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = intOpt_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = intOpt_nil   as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'

// let _ = ... as String?
//if let _ = str_value    as String? { true } // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//if let _ = strOpt_value as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_nil   as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = int_value    as String? { true } // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//if let _ = intOpt_value as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_nil   as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?

// let _ = ... as? String?
//if let _ = str_value    as? String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  if let _ = strOpt_value as? String? { true } // true
  if let _ = strOpt_nil   as? String? { true } // true
//if let _ = int_value    as? String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  if let _ = intOpt_value as? String? { true } // false
  if let _ = intOpt_nil   as? String? { true } // true

// let _ = ... as! String?
//if let _ = str_value    as! String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  if let _ = strOpt_value as! String? { true } // true
  if let _ = strOpt_nil   as! String? { true } // false
//if let _ = int_value    as! String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
//if let _ = intOpt_value as! String? { true } // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
  if let _ = intOpt_nil   as! String? { true } // false

Swift 2.0:

import UIKit

let str:    AnyObject   = String("abc")
let strOpt: AnyObject?  = String("abc")
let strNil: AnyObject?  = (nil as String?)
let int:    AnyObject   = Int(1)
let intOpt: AnyObject?  = Int(1)
let intNil: AnyObject?  = (nil as Int?)

str    as? String // == "abc"
strOpt as? String // == "abc"
strNil as? String // == nil
int    as? String // == nil
intOpt as? String // == nil
intNil as? String // == nil

str    as! String? // Compile-Time Error: Cannot downcast from 'AnyObject' to a more optional type 'String?'
strOpt as! String? // == "abc"
strNil as! String? // == nil
int    as! String? // Compile-Time Error: Cannot downcast from 'AnyObject' to a more optional type 'String?'
intOpt as! String? // Run-Time Error: Could not cast value of type '__NSCFNumber' to 'NSString'
intNil as! String? // == nil
11
Senseful

as? Types - signifie que le processus de down casting est optionnel. Le processus peut être réussi ou non (le système renverra zéro si l'échec de la diffusion vers le bas échoue).

as! Type? - Ici, le processus de down casting devrait être réussi (! indique que) . Le point d'interrogation final indique si le résultat final peut être nul ou non.

Plus d'infos concernant "!" Et "?"

Prenons 2 cas

  1. Considérer:

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell
    

    Ici, nous ne savons pas si le résultat de la conversion d'une cellule avec l'identificateur "Cell" en UITableViewCell est un succès ou non. Si échec, il renvoie nil (nous évitons donc le plantage ici). Ici, nous pouvons faire comme indiqué ci-dessous.

    if let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell {
        // If we reached here it means the down casting was successful
    }
    else {
        // unsuccessful down casting
    }
    

    Souvenons-nous donc comme ceci - Si ? _ cela signifie que nous ne sommes pas sûrs que la valeur soit nulle ou non (un point d’interrogation apparaît lorsque nous ne savons rien).

  2. Contraste cela pour:

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell. 
    

    Ici, nous disons au compilateur que le down casting devrait réussir. Si cela échoue, le système se bloquera. Nous donnons donc ! lorsque nous sommes sûrs que la valeur est non nulle.

11
jishnu bala

Ce sont deux formes différentes de Downcasting dans Swift.

(as?) , qui est connu pour être le formulaire conditionnel , renvoie une valeur optionnelle du type que vous essayez abaisser à.

Vous pouvez l'utiliser quand vous n'êtes pas sûr que le downcast réussira. Cette forme d'opérateur retournera toujours une valeur optionnelle, et la valeur sera nulle si le downcast n'était pas possible. Cela vous permet de vérifier que le downcast est réussi.


(as!) , qui est connu pour être la forme forcée , tente l’abattement et force le résultat sous la forme une seule action composée.

Vous devez l’utiliser [~ # ~] uniquement [~ # ~] lorsque vous êtes certain que le downcast réussira toujours. Cette forme d’opérateur déclenchera une erreur d’exécution si vous essayez de convertir un type de classe incorrect.

Pour plus de détails, consultez la section Type Casting de la documentation Apple.

8
Scott Zhu
  • as utilisé pour la conversion en amont et la conversion de type en type ponté
  • as? utilisé pour la coulée en toute sécurité, retourne zéro si échoué
  • as! utilisé pour forcer le casting, planter en cas d'échec

Remarque:

  • as! ne peut pas convertir le type brut en option

Exemples:

let rawString: AnyObject = "I love Swift"
let optionalString: AnyObject? = "we love Swift"
let nilString: AnyObject? = (nil as String?)

let rawInt: AnyObject = Int(3)
let optionalInt: AnyObject? = Int(3)
let nilInt: AnyObject? = (nil as Int?)

Exemple

var age: Int? = nil
var height: Int? = 180

En ajoutant un ? immédiatement après le type de données, vous indiquez au compilateur que la variable peut contenir un nombre ou non. Soigné! Notez qu’il n’a pas vraiment de sens de définir des constantes facultatives - vous ne pouvez définir leur valeur qu’une seule fois et vous êtes donc en mesure de dire si leur valeur sera nulle ou non.

Quand devrions-nous utiliser "?" et quand "!"

disons que nous avons une application simple basée sur UIKit. nous avons du code dans notre contrôleur de vue et voulons présenter un nouveau contrôleur de vue par dessus. et nous devons décider d’afficher la nouvelle vue à l’écran à l’aide du contrôleur de navigation.

Comme nous le savons, chaque instance ViewController a un contrôleur de navigation de propriété. Si vous construisez une application basée sur un contrôleur de navigation, cette propriété du contrôleur de vue principal de votre application est définie automatiquement et vous pouvez l’utiliser pour les contrôleurs de vue Push ou Pop. Si vous utilisez un seul modèle de projet d’application, aucun contrôleur de navigation ne sera créé automatiquement pour vous. Par conséquent, aucun contrôleur de vue par défaut de votre application ne sera stocké dans la propriété navigationController.

Je suis sûr que vous avez déjà deviné qu’il s’agissait exactement d’un type de données facultatif. Si vous cochez UIViewController, vous verrez que la propriété est définie comme suit:

var navigationController: UINavigationController? { get }

Revenons donc à notre cas d’utilisation. Si vous savez que votre contrôleur de vue aura toujours un contrôleur de navigation, vous pouvez le forcer à le dérouler:

controller.navigationController!.pushViewController(myViewController, animated: true)

Quand vous mettez un! derrière le nom de la propriété, vous indiquez au compilateur Peu m'importe que cette propriété soit optionnelle, je sais que lorsque ce code sera exécuté, il y aura toujours un magasin de valeurs. Traitez donc cette option comme une type de données normal. Eh bien, n'est-ce pas agréable? Mais que se passerait-il si aucun contrôleur de navigation n’était associé à votre contrôleur de vue? Si vous suggérez qu'il y aura toujours une valeur stockée dans navigationController était faux? Votre application va planter. Simple et moche comme ça.

Alors, utilisez! seulement si vous êtes sûr à 101% que cela est sans danger.

Et si vous n’êtes pas sûr qu’il y aura toujours un contrôleur de navigation? Ensuite, vous pouvez utiliser? au lieu d'une !:

controller.navigationController?.pushViewController(myViewController, animated: true)

Qu'est-ce que derrière le nom de la propriété indique au compilateur est Je ne sais pas si cette propriété contient nil ou une valeur, donc: si elle a une valeur, utilisez-la et considérez simplement l'expression entière nil . Effectivement le? vous permet d'utiliser cette propriété dans le cas où il y aurait un contrôleur de navigation. Non, si contrôles ou castings quelconques. Cette syntaxe est parfaite lorsque vous ne voulez pas savoir si vous avez un contrôleur de navigation ou non, et que vous voulez faire quelque chose que s’il en existe.

Grand merci à Fantageek

7
swiftBoy

Peut-être que cet exemple de code aidera quelqu'un à comprendre le principe:

var dict = [Int:Any]()
dict[1] = 15

let x = dict[1] as? String
print(x) // nil because dict[1] is an Int

dict[2] = "Yo"

let z = dict[2] as! String?
print(z) // optional("Yo")
let zz = dict[1] as! String // crashes because a forced downcast fails


let m = dict[3] as! String?
print(m) // nil. the forced downcast succeeds, but dict[3] has no value
3
smileBot

Il peut être plus facile de retenir le modèle pour ces opérateurs dans Swift comme: ! implique "cela pourrait piéger", tandis que ? indique "cela pourrait être nul".

reportez-vous à: https://developer.Apple.com/Swift/blog/?id=2

0
Zgpeace

Le premier est un "transtypage conditionnel" (voir sous "opérateurs de transtypage" dans la documentation que j'ai liée) . Si la conversion réussit, la valeur de l'expression est encapsulée dans une option et renvoyée, sinon la valeur renvoyée est nil.

Le second signifie que optionalString peut être un objet chaîne ou nil.

Plus d'informations trouvées dans cette question connexe .

0
Michael Dautermann