ios - ¿Qué es un "valor desenvuelto" en Swift?
ios8 (4)
Estoy aprendiendo Swift para iOS 8 / OSX 10.10 siguiendo este tutorial , y el término " valor desenvuelto " se usa varias veces, como en este párrafo (en Objetos y Clase ):
Cuando trabaja con valores opcionales, ¿puede escribir? antes de operaciones como métodos, propiedades y subíndices. Si el valor antes de la? es nil, todo después de? se ignora y el valor de la expresión completa es nulo. De lo contrario, el valor opcional se desenvuelve y todo después de? actúa sobre el valor desenvuelto . En ambos casos, el valor de la expresión completa es un valor opcional.
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
No lo entiendo, y busqué en la web sin suerte.
¿Qué significa esto?
Editar
De la respuesta de Cezary, hay una ligera diferencia entre la salida del código original y la solución final (probado en el patio de recreo):
Código original
La solución de Cezary
Las propiedades de la superclase se muestran en el resultado en el segundo caso, mientras que hay un objeto vacío en el primer caso.
¿No se supone que el resultado es idéntico en ambos casos?
Preguntas y respuestas relacionadas: ¿Qué es un valor opcional en Swift?
La respuesta correcta existente es genial, pero descubrí que para que yo pudiera entender esto por completo, necesitaba una buena analogía, ya que este es un concepto muy abstracto y extraño.
Por lo tanto, permítanme ayudar a esos desarrolladores compañeros "de cerebro derecho" (pensamiento visual), dándoles una perspectiva diferente además de la respuesta correcta. Aquí hay una buena analogía que me ayudó mucho.
Aniversario de envoltura de regalo de cumpleaños
Piense en las opciones como obsequios de cumpleaños que vienen en envoltorios rígidos, duros y coloreados.
No sabes si hay algo dentro del envoltorio hasta que desenvuelves el regalo. ¡Tal vez no haya nada dentro! Si hay algo adentro, podría ser otro regalo, que también está envuelto, y que también podría no contener nada . Incluso podría desenvolver 100 regalos anidados para finalmente descubrir que no había nada más que envolver .
Si el valor de la opción no es nil
, ahora ha revelado una casilla que contiene algo . Pero, especialmente si el valor no está tipeado explícitamente y es una variable y no una constante predefinida, entonces es posible que aún necesite abrir la caja antes de que pueda saber algo específico sobre lo que hay en la caja, como qué tipo es, o qué el valor real es
¡¿Qué hay en la caja?! Analogía
Incluso después de desenvolver la variable, sigues siendo como Brad Pitt en la última escena en SE7EN ( advertencia : spoilers y lenguaje sucio y violencia de clasificación R), porque incluso después de haber desenvuelto el presente, te encuentras en la siguiente situación: ahora tiene nil
, o una caja que contiene algo (pero no sabe qué).
Usted puede saber el tipo de algo . Por ejemplo, si declara que la variable es de tipo, [Int:Any?]
, Entonces sabrá que tiene un diccionario (potencialmente vacío) con subíndices enteros que arrojan contenidos envueltos de cualquier tipo antiguo.
Es por eso que lidiar con los tipos de colección (Diccionarios y Matrices) en Swift puede ser un tanto peliagudo.
Caso en punto:
typealias presentType = [Int:Any?]
func wrap(i:Int, gift:Any?) -> presentType? {
if(i != 0) {
let box : presentType = [i:wrap(i-1,gift:gift)]
return box
}
else {
let box = [i:gift]
return box
}
}
func getGift() -> String? {
return "foobar"
}
let f00 = wrap(10,gift:getGift())
//Now we have to unwrap f00, unwrap its entry, then force cast it into the type we hope it is, and then repeat this in nested fashion until we get to the final value.
var b4r = (((((((((((f00![10]! as! [Int:Any?])[9]! as! [Int:Any?])[8]! as! [Int:Any?])[7]! as! [Int:Any?])[6]! as! [Int:Any?])[5]! as! [Int:Any?])[4]! as! [Int:Any?])[3]! as! [Int:Any?])[2]! as! [Int:Any?])[1]! as! [Int:Any?])[0])
//Now we have to DOUBLE UNWRAP the final value (because, remember, getGift returns an optional) AND force cast it to the type we hope it is
let asdf : String = b4r!! as! String
print(asdf)
Primero, debes entender qué es un tipo opcional. Un tipo opcional básicamente significa que la variable puede ser nil
.
Ejemplo:
var canBeNil : Int? = 4
canBeNil = nil
El signo de interrogación indica el hecho de que canBeNil
puede ser nil
.
Esto no funcionaría:
var cantBeNil : Int = 4
cantBeNil = nil // can''t do this
Para obtener el valor de su variable si es opcional, debe desenvolverlo . Esto solo significa poner un signo de exclamación al final.
var canBeNil : Int? = 4
println(canBeNil!)
Tu código debería verse así:
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare!.sideLength
Una nota al margen:
También puede declarar opcionales para desenvolver automáticamente utilizando un signo de exclamación en lugar de un signo de interrogación.
Ejemplo:
var canBeNil : Int! = 4
print(canBeNil) // no unwrapping needed
Entonces, una forma alternativa de arreglar tu código es:
let optionalSquare: Square! = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare.sideLength
EDITAR:
La diferencia que está viendo es exactamente el síntoma del hecho de que el valor opcional está ajustado . Hay otra capa encima. La versión desenvuelta solo muestra el objeto directo porque, bueno, está desenvuelto.
Una comparación rápida de juegos:
En los casos primero y segundo, el objeto no se desenrolla automáticamente, por lo que puede ver dos "capas" ( {{...}}
), mientras que en el tercer caso, solo verá una capa ( {...}
) porque el objeto se está desenrollando automáticamente.
La diferencia entre el primer caso y el segundo dos casos es que los segundos dos casos le darán un error de tiempo de ejecución si optionalSquare
se establece en nil
. Usando la sintaxis en el primer caso, puedes hacer algo como esto:
if let sideLength = optionalSquare?.sideLength {
println("sideLength is not nil")
} else {
println("sidelength is nil")
}
Swift otorga una gran importancia a la seguridad de tipo. Todo el lenguaje Swift fue diseñado teniendo en cuenta la seguridad. Es uno de los sellos distintivos de Swift y uno que deberías recibir con los brazos abiertos. Ayudará en el desarrollo de código limpio y legible, y ayudará a evitar que su aplicación falle.
Todos los opcionales en Swift están demarcados con el ?
símbolo. Al configurar el ?
después del nombre del tipo en el que está declarando como opcional, ¿está esencialmente emitiendo esto no como el tipo en el que está antes del ?
, sino como el tipo opcional .
Nota: ¿Una variable o tipo
Int
no es lo mismo queInt?
. Son dos tipos diferentes que no se pueden operar entre sí.
Usando Opcional
var myString: String?
myString = "foobar"
Esto no significa que estés trabajando con un tipo de String
. Esto significa que está trabajando con un tipo de String?
(Cadena opcional, o cadena opcional). De hecho, cada vez que intentas
print(myString)
en el tiempo de ejecución, la consola de depuración imprimirá Optional("foobar")
. La parte " Optional()
" indica que esta variable puede o no tener un valor en tiempo de ejecución, pero sucede que actualmente contiene la cadena "foobar". Esta indicación de " Optional()
" permanecerá, a menos que haga lo que se denomina "desempaquetar" el valor opcional.
Desenvolver una opción significa que ahora está lanzando ese tipo como no opcional. Esto generará un nuevo tipo y asignará el valor que residía dentro de ese tipo opcional al nuevo no opcional. De esta forma puede realizar operaciones en esa variable, ya que el compilador ha garantizado que tiene un valor sólido.
Desenvolviendo condicional verificará si el valor en el opcional es nil
o no. Si no es nil
, habrá una variable constante recién creada a la que se le asignará el valor y se desenvolverá en la constante no opcional. Y desde allí puede usar de forma segura lo no opcional en el bloque if
.
Nota: Puede asignarle a su constante no envuelta condicional el mismo nombre que la variable opcional que está desenvolviendo.
if let myString = myString {
print(myString)
// will print "foobar"
}
La opción más limpia para acceder al valor de una opción es abrir de manera opcional las opciones opcionales, ya que si contiene un valor nulo, no se ejecutará todo dentro del bloque if let. Por supuesto, como cualquier instrucción if, puede incluir un bloque else
if let myString = myString {
print(myString)
// will print "foobar"
}
else {
print("No value")
}
¡Desenvolver a la fuerza se hace empleando lo que se conoce como el !
("bang") operador. Esto es menos seguro pero aún permite que su código compile. Sin embargo, cada vez que utilice el operador de bang, debe tener un 1000% de certeza de que su variable, de hecho, contiene un valor sólido antes de desenvolverse a la fuerza.
var myString: String?
myString = "foobar"
print(myString!)
Esto de arriba es un código Swift completamente válido. Imprime el valor de myString
que se configuró como "foobar". El usuario vería foobar
impreso en la consola y eso es todo. Pero supongamos que nunca se estableció el valor:
var myString: String?
print(myString!)
Ahora tenemos una situación diferente en nuestras manos. A diferencia de Objective-C, cada vez que se intenta forzar el desenvolvimiento opcional de una opción, y la opción opcional no se ha establecido y es nil
, cuando intente desenvolver la opción para ver qué hay dentro de la aplicación se bloqueará.
Desenvolviendo con Tipo de fundición . Como dijimos anteriormente, mientras está unwrapping
el optativo, en realidad está unwrapping
a un tipo no opcional, también puede convertir el no opcional en un tipo diferente. Por ejemplo:
var something: Any?
En algún lugar de nuestro código, la variable something
se establecerá con algún valor. Tal vez estamos usando genéricos o tal vez hay alguna otra lógica que está sucediendo que hará que esto cambie. Entonces, más adelante en nuestro código, queremos usar something
pero aún así poder tratarlo de manera diferente si es un tipo diferente. En este caso, querrá usar la palabra clave as
para determinar esto:
Nota: El operador
as
es la forma en que escribe el molde en Swift.
// Conditionally
if let thing = something as? Int {
print(thing) // 0
}
// Optionally
let thing = something as? Int
print(thing) // Optional(0)
// Forcibly
let thing = something as! Int
print(thing) // 0, if no exception is raised
Observe la diferencia entre los dos as
palabras clave. Al igual que antes, cuando desenvolvimos a la fuerza una opción opcional, utilizamos la !
bang operador para hacerlo. Aquí harás lo mismo, pero en lugar de lanzar solo como no opcional, lo estás lanzando también como Int
. Y debe ser capaz de ser downcast como Int
, de lo contrario, como usar el operador bang cuando el valor es nil
su aplicación se bloqueará.
Y para usar estas variables en algún tipo de operación matemática, deben desenvolverlas para poder hacerlo.
Por ejemplo, en Swift solo se pueden operar entre sí tipos de datos numéricos válidos del mismo tipo. Cuando lanzas un tipo con el as!
Usted está forzando el abatimiento de esa variable como si estuviera seguro de que es de ese tipo, por lo tanto, es seguro operar y no bloquear su aplicación. Esto está bien siempre que la variable sea del tipo al que lo estás lanzando, de lo contrario tendrás un lío en tus manos.
¡Sin embargo, lanzando con as!
permitirá que tu código compile. Al lanzar con un as?
es una historia diferente De hecho, as?
declara su Int
como un tipo de datos completamente diferente, todos juntos.
Ahora es Optional(0)
Y si alguna vez trataste de hacer tu tarea escribiendo algo así como
1 + Optional(1) = 2
Su profesor de matemáticas probablemente le habría dado una "F". Lo mismo con Swift. Excepto que Swift preferiría no compilar en absoluto en lugar de darte una calificación. Porque al final del día, la opción opcional puede ser nula .
Safety First Kids.