Options de Downcasting dans Swift: as? Type, ou en tant que! Le Type?

étant donné ce qui suit dans Swift:

var optionalString: String?
let dict = NSDictionary()

Quelle est la différence pratique entre les deux énoncés suivants:

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

vs

optionalString = dict.objectForKey("SomeKey") as! String?
78
demandé sur sdduursma 0000-00-00 00:00:00

9 réponses

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 assignera simplement nil à l'optionnel.

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

cela dit, je savoir cette chose est un String? . Il en résultera aussi que optionalString est de type String? , mais il s'écrasera si le type sous-jacent est autre chose.

le premier style est ensuite utilisé avec if let pour déballer en toute sécurité l'option:

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)
}
124
répondu vacawama 2018-06-21 14:33:47

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
10
répondu Senseful 2017-01-27 22:37:21

as? Types - le procédé de coulée vers le bas est facultatif. Le processus peut être réussi ou non(le système retournera zéro si la coulée vers le bas échoue).Aucun moyen ne s'écrasera si down casting échoue.

as! Type? - ici, le procédé de coulée vers le bas devrait être couronné de succès ( ! indique que) . Le point d'interrogation final indique si le résultat final peut être nul ou non.

Plus d'infos à propos des "!"et "?"

prenons 2 cas

  1. prendre en considération:

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

    ici, nous ne savons pas si le résultat de la fonte de la cellule avec l'identificateur "Cell" à Uitable Viewcell est le succès ou non. En cas d'échec, il renvoie zéro( donc nous évitons de nous écraser 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
    }
    

    alors souvenons - nous comme ceci- si ? cela signifie que nous ne sommes pas sûr si la valeur est nulle ou pas (le point d'interrogation vient quand on ne sait pas des choses).

  2. contraste avec:

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

    ici, nous disons au compilateur que la coulée vers le bas devrait être réussie. Si elle échoue, le système se bloque. Nous donnons donc ! quand nous sommes sûrs que la valeur n'est pas nulle.

10
répondu jishnu bala 2018-02-11 18:06:47

il s'agit de deux formes différentes de Downcasting dans Swift.

( as? ) , qui est connu pour être le Conditionnel , renvoie une valeur optionnelle de type que vous essayez d'baissés.

Vous pouvez l'utiliser lorsque vous n'êtes pas sûr si les humbles va réussir. Cette forme de l'opérateur retourne toujours une valeur facultative, et valeur sera nul si les humbles n'était pas possible. Cela permet vous recherchez un succès abattu.


( as! ) , qui est connu pour être la forme forcée , tente le downcast et force-déroule le résultat comme une action composée simple.

Vous devriez l'utiliser SEULEMENT lorsque vous êtes sûr que les humbles seront toujours réussir. Cette forme de l'opérateur déclenche une exécution erreur si vous essayez de downcast à un type de classe incorrect.

pour plus de détails, veuillez consulter la section Type Casting de la documentation D'Apple.

7
répondu Scott Zhu 2015-07-08 10:20:23
  • as utilisé pour l'upcasting et le type de coulée à type ponté
  • as? utilisé pour la coulée sûre, retour néant en cas de défaillance
  • as! utilisé pour forcer le moulage, se crash if failed

Note:

  • as! ne pouvez pas jeter raw type facultatif

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 dites au compilateur que la variable peut contenir un nombre ou pas. Soigné! Notez qu'il n'est pas vraiment logique de définir des constantes optionnelles – vous pouvez définir leur valeur qu'une seule fois et donc vous seriez en mesure de dire si leur valeur sera nulle ou non.

Quand on doit utiliser "?"et lorsque "!"

disons que nous avons UIKit basé application simple. nous avons du code dans notre contrôleur de vue et nous voulons présenter un nouveau contrôleur de vue dessus. et nous devons décider de pousser la nouvelle vue sur l'écran en utilisant le contrôleur de navigation.

comme nous le savons, chaque instance de ViewController possède 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 maître de votre application est réglée automatiquement et vous pouvez l'utiliser pour pousser ou contrôleurs de vue pop. Si vous utilisez un modèle de projet app unique – il n'y aura pas de contrôleur de navigation créé automatiquement pour vous, de sorte que le contrôleur de vue par défaut de votre application n'aura rien stocké dans la propriété navigationController.

je suis sûr que vous avez déjà deviné que c'est exactement un cas pour un type de données optionnel. Si vous vérifiez UIViewController vous verrez que la propriété est définie comme:

var navigationController: UINavigationController? { get }

alors revenons à notre cas d'utilisation. Si vous savez pour un fait que votre contrôleur de vue aura toujours un contrôleur de navigation, vous pouvez aller de l'avant et forcer la décompression:

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

Quand vous mettez un ! derrière le nom de la propriété, vous dites au compilateur je me fiche que cette propriété soit optionnelle, je sais que lorsque ce code s'exécute il y aura toujours un magasin de valeurs alors traitez cette optionnelle comme un type de données normal. N'est-ce pas agréable? Ce serait se produit cependant s'il n'y a pas un contrôleur de navigation à votre contrôleur de vue? Si vous suggérez qu'il y aura toujours une valeur stockée dans navigationController était erroné? Votre application va s'écraser. Simple et laid comme ça.

donc, utilisez ! seulement si vous êtes 101% sûr que c'est sûr.

et si vous n'êtes pas sûr qu'il y aura toujours un contrôleur de navigation? Vous pouvez ensuite utiliser ? au lieu d'un !:

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

que se passe-t-il ? derrière le nom de la propriété dit le compilateur est Je ne sais pas si cette propriété contient zéro ou une valeur, donc: si elle a de la valeur l'utiliser, et oterwise juste considérer l'expression entière nul. effectivement le ? vous permet d'utiliser cette propriété dans le cas où il y a une manette de navigation. Aucun, si des contrôles de toute nature ou de coulées de toute sorte. Cette syntaxe est parfaite quand vous ne vous souciez pas si vous avez un contrôleur de navigation ou pas, et veulent faire quelque chose que s'il ya.

Merci beaucoup à Fantageek

5
répondu swiftBoy 2016-03-31 20:28:47

peut-être que cet exemple de code aidera quelqu'un grok 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
2
répondu smileBot 2015-07-03 18:54:25

la première est une "coulée conditionnelle" (regardez sous "opérateurs de coulée par type" dans la documentation que j'ai liée) . Si la fonte réussit, la valeur de l'expression est enveloppée dans une option et retournée, sinon la valeur retournée est nulle.

la seconde signifie que optionalString pourrait être un objet string ou il pourrait être nul.

pour en savoir plus, lisez la question connexe .

0
répondu Michael Dautermann 2017-05-23 12:02:44

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

se référer à: https://developer.apple.com/swift/blog/?id=23

0
répondu 陈保状 2016-03-04 14:06:28

je suis novice pour Swift et j'écris cet exemple en essayant d'expliquer ce que je comprends au sujet des "optionnels". Si je me trompe merci de me corriger.

Merci.


class Optional {

    var lName:AnyObject! = "1"

    var lastName:String!
}

let obj = Optional()

print(obj.lName)

print(obj.lName!)

obj.lastName = obj.lName as? String

print(obj.lastName)

(1) : obj.lastName = obj.lName as! String

vs

(2) : obj.lastName = obj.lName as? String

Ans: (1) ici programmeur est damm sûr que “obj.lName” contient le type de chaîne de caractères objet. Donnez cette valeur à “obj.lastName” .

maintenant, si programmeur est correct signifie "obj.lName" est un objet de type chaîne, alors pas de problème. "obj.lastName " se positionnera à la même valeur.

mais si le programmeur a tort signifie "obj.lName" n'est pas un objet de type chaîne, c'est-à-dire qu'il contient un autre objet de type comme" NSNumber " etc. Puis CRASH (erreur de temps D'exécution).

(2) Programmeur n'est pas sûr que “obj.lName” contient chaîne tapez objet ou n'importe quel autre type d'objet. Ainsi, définissez cette valeur à “obj.lastName” si c'est un type de chaîne.

maintenant, si programmeur est correct signifie “obj.lName” est un objet de type chaîne, alors pas de problème. “obj.lastName” définira la même valeur.

mais si programmeur se trompe signifie obj.lName n'est pas un objet de type string, c'est-à-dire qu'il contient un autre objet de type comme "NSNumber" , etc. Ensuite “obj.lastName” sera mis à la valeur nulle. Donc, Pas D'Accident (Heureux:)

-1
répondu iPhoneBuddy 2016-09-02 11:37:35