has - Downcasting opcional en Swift: ¿como? Escriba, o como! ¿Tipo?
generic var swift (9)
La diferencia práctica es esta:
var optionalString = dict["SomeKey"] as? String
optionalString
será una variable de tipo String?
. Si el tipo subyacente es algo distinto de una String
esto inofensivamente solo asignará nil
a la opción.
var optionalString = dict["SomeKey"] as! String?
Esto dice, sé que esto es una String?
. ¿Esto también dará lugar a que optionalString
sea de tipo String?
, pero se bloqueará si el tipo subyacente es otra cosa.
El primer estilo se usa con if let
de desenvolver de forma segura el opcional:
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)
}
Dado lo siguiente en Swift:
var optionalString: String?
let dict = NSDictionary()
¿Cuál es la diferencia práctica entre las dos afirmaciones siguientes?
optionalString = dict.objectForKey("SomeKey") as? String
vs
optionalString = dict.objectForKey("SomeKey") as! String?
Para aclarar lo que dijo vacawama, aquí hay un ejemplo ...
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
Puede ser más fácil recordar el patrón para estos operadores en Swift como !
implica que "esto podría atrapar", ¿mientras ?
indica "esto podría ser nulo".
Son dos formas diferentes de Downcasting en Swift.
( as?
) , que se sabe que es el Formulario condicional , devuelve un valor opcional del tipo al que está tratando de abatir.
Puede usarlo cuando no esté seguro si el downcast tendrá éxito. Esta forma del operador siempre devolverá un valor opcional, y el valor será nulo si el downcast no fue posible. Esto le permite verificar si hay un downcast exitoso.
( as!
) , que se sabe que es la Forma Forzada , intenta abatir y fuerza el resultado como una sola acción compuesta.
Debe usarlo SÓLO cuando esté seguro de que el abatimiento siempre tendrá éxito. Esta forma del operador activará un error de tiempo de ejecución si intenta bajar a un tipo de clase incorrecto.
Para obtener más información, consulte la sección Tipo de transmisión de la documentación de Apple.
Soy un novato de Swift y escribo este ejemplo tratando de explicar lo que entiendo acerca de ''opcionales''. Si estoy equivocado, por favor corrígeme.
Gracias.
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
obj.lastName = obj.lName as! String
vs
(2): obj.lastName = obj.lName as? String
obj.lastName = obj.lName as? String
Resp .: (1) Aquí el programador está seguro de que “obj.lName”
contiene el tipo de objeto de cadena. Así que solo dale ese valor a “obj.lastName”
.
Ahora, si el programador es correcto significa que "obj.lName"
es un tipo de objeto de cadena, entonces no hay problema. "obj.lastName" se establecerá en el mismo valor.
Pero si el programador está equivocado significa que "obj.lName"
no es un tipo de objeto de cadena, es decir, que contiene algún otro tipo de objeto como "NSNumber", etc. Luego, CRASH (Error de tiempo de ejecución).
(2) El programador no está seguro de que “obj.lName”
contenga el tipo de objeto de cadena o cualquier otro tipo de objeto. Así que establezca ese valor en “obj.lastName”
si es un tipo de cadena.
Ahora, si el programador es correcto significa que “obj.lName”
es un tipo de objeto de cadena, entonces no hay problema. “obj.lastName”
se establecerá en el mismo valor.
Pero si el programador está equivocado significa que obj.lName no es un tipo de objeto de cadena, es decir, que contiene algún otro tipo de objeto como "NSNumber"
etc. Entonces “obj.lastName”
establecerá el valor nulo. Entonces, No Crash (Feliz :)
Tal vez este ejemplo de código ayude a alguien a asimilar el principio:
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
El primero es un "molde condicional" (mira bajo "operadores de tipografía" en la documentación que he vinculado) . Si el lanzamiento tiene éxito, el valor de la expresión se envuelve de forma opcional y se devuelve; de lo contrario, el valor devuelto es nulo.
El segundo significa que opcionalString podría ser un objeto de cadena o podría ser nulo.
as? Types
as? Types
: significa que el proceso de lanzamiento descendente es opcional. El proceso puede ser exitoso o no (el sistema devolverá nil si falla el fundido descendente). Cualquier manera no se bloqueará si falla el fundido descendente.
as! Type?
- Aquí el proceso de down casting debería ser exitoso ( !
indica). El signo de interrogación final indica si el resultado final puede ser nulo o no.
Más información sobre "!" y "?"
Tomemos 2 casos
Considerar:
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell
Aquí no sabemos si el resultado del down casting de la celda con el identificador "Cell" a UITableViewCell es exitoso o no. Si no tiene éxito, devuelve nil (por lo que evitamos el bloqueo aquí). Aquí podemos hacer lo que se indica a continuación.
if let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell { // If we reached here it means the down casting was successful } else { // unsuccessful down casting }
Así que recordemos esto así -
?
Si?
significa que no estamos seguros de si el valor es nulo o no (el signo de interrogación aparece cuando no conocemos las cosas).Contraste eso a:
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell.
Aquí le decimos al compilador que el lanzamiento descendente debería ser exitoso. Si falla, el sistema se bloqueará. ¡Entonces damos
!
cuando estamos seguros de que el valor no es nulo.
-
as
usa para la conversión ascendente y tipo de conversión a tipo puenteado -
as?
utilizado para la fundición segura, devuelva nil si falló -
as!
usado para forzar el casting, fallar si falla
Nota:
-
as!
no se puede convertir el tipo sin procesar en opcional
Ejemplos:
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?)
Ejemplo
var age: Int? = nil
var height: Int? = 180
Al agregar un ? inmediatamente después del tipo de datos le dice al compilador que la variable puede contener un número o no. ¡Ordenado! Tenga en cuenta que realmente no tiene sentido definir constantes opcionales: puede establecer su valor solo una vez y, por lo tanto, podrá decir si su valor será nulo o no.
Cuando deberíamos usar "?" y cuando "!"
digamos que tenemos una aplicación simple basada en UIKit. tenemos un código en nuestro controlador de vista y queremos presentar un nuevo controlador de vista encima. y necesitamos decidir empujar la nueva vista en la pantalla usando el controlador de navegación.
Como sabemos, cada instancia de ViewController tiene un controlador de navegación de propiedad. Si está construyendo una aplicación basada en un controlador de navegación, esta propiedad del controlador de vista maestra de su aplicación se configura automáticamente y puede usarla para presionar o abrir los controladores de vista. Si usa una sola plantilla de proyecto de aplicación, no habrá un controlador de navegación creado automáticamente para usted, de modo que el controlador de vista predeterminado de su aplicación no tendrá nada almacenado en la propiedad del control de navegación.
Estoy seguro de que ya adivinó que este es exactamente el caso para un tipo de datos opcional. Si marca UIViewController, verá que la propiedad se define como:
var navigationController: UINavigationController? { get }
Volvamos a nuestro caso de uso. Si sabes a ciencia cierta que tu controlador de vista siempre tendrá un controlador de navegación, puedes continuar y forzarlo a desenvolver:
controller.navigationController!.pushViewController(myViewController, animated: true)
Cuando pones un! detrás del nombre de la propiedad le dices al compilador que no me importa que esta propiedad sea opcional, sé que cuando se ejecuta este código siempre habrá un almacén de valores, así que trata este opcional como un tipo de datos normal. Bueno, no es tan bueno? ¿Qué pasaría si no hubiera un controlador de navegación para su controlador de visualización? Si su sugerencia de que siempre habrá un valor almacenado en navigationController fue incorrecto? Tu aplicación se bloqueará. Simple y feo como eso.
Entonces, ¡usa! solo si estás 101% seguro de que esto es seguro.
¿Qué tal si no estás seguro de que siempre habrá un controlador de navegación? Entonces puedes usar? en lugar de un !:
controller.navigationController?.pushViewController(myViewController, animated: true)
Que ? detrás del nombre de la propiedad le dice al compilador que no sé si esta propiedad contiene nil o un valor, entonces: si tiene valor úsela, y de otra manera simplemente considere nula toda la expresión. Efectivamente el? le permite usar esa propiedad solo en el caso de que haya un controlador de navegación. No, si se trata de cheques de cualquier tipo o piezas fundidas de cualquier tipo. Esta sintaxis es perfecta cuando no te importa si tienes un controlador de navegación o no, y quieres hacer algo solo si lo hay.
Enormes gracias a Fantageek