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?
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)
}
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
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
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).
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.
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.
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'échecRemarque:
as!
ne peut pas convertir le type brut en optionlet 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.
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
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
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
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.