¿Qué significa un signo de exclamación en el lenguaje Swift?
La guía del lenguaje de programación Swift tiene el siguiente ejemplo:
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { println("\(name) is being deinitialized") }
}
class Apartment {
let number: Int
init(number: Int) { self.number = number }
var tenant: Person?
deinit { println("Apartment #\(number) is being deinitialized") }
}
var john: Person?
var number73: Apartment?
john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)
//From Apple's “The Swift Programming Language” guide (https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html)
Luego, al asignar el apartamento a la persona, utilizan un signo de exclamación para "desenvolver la instancia":
john!.apartment = number73
¿Qué significa "desenvolver la instancia"? ¿Por qué es necesario? ¿En qué se diferencia de simplemente hacer lo siguiente?
john.apartment = number73
Soy muy nuevo en el lenguaje Swift. Sólo trato de entender lo básico.
ACTUALIZACIÓN:
La gran pieza del rompecabezas que me faltaba (no indicada directamente en las respuestas, al menos no al momento de escribir esto) es cuando haces lo siguiente:
var john: Person?
eso NO significa que " john
es de tipo Person
y podría ser nulo", como pensé originalmente. Simplemente lo entendí mal Person
y Person?
son tipos completamente separados. Una vez que entendí eso, todo el resto ?
, !
la locura y las excelentes respuestas a continuación tuvieron mucho más sentido.
¿Qué significa "desenvolver la instancia"? ¿Por qué es necesario?
Hasta donde puedo entender (esto también es muy nuevo para mí)...
El término "envuelto" implica que debemos pensar en una variable opcional como un regalo, envuelto en papel brillante, que podría (¡tristemente!) estar vacío .
Cuando está "envuelto", el valor de una variable opcional es una enumeración con dos valores posibles (un poco como un booleano). Esta enumeración describe si la variable tiene un valor ( Some(T)
) o no ( None
).
Si hay un valor, este se puede obtener "desenvolviendo" la variable (obteniendo T
from Some(T)
).
¿ En qué se
john!.apartment = number73
diferencia dejohn.apartment = number73
? (Parafraseado)
Si escribe el nombre de una variable opcional (por ejemplo, texto john
, sin !
), esto se refiere a la enumeración "ajustada" (Algunos/Ninguno), no al valor en sí (T). Entonces john
no es una instancia de Person
y no tiene un apartment
miembro:
john.apartment
// 'Person?' does not have a member named 'apartment'
El valor real Person
se puede desenvolver de varias maneras:
- "desempaquetado forzado":
john!
(da elPerson
valor si existe, error de tiempo de ejecución si es nulo) - "enlace opcional":
if let p = john { println(p) }
(ejecutaprintln
si el valor existe) - "encadenamiento opcional":
john?.learnAboutSwift()
(ejecuta este método inventado si el valor existe)
Supongo que eliges una de estas formas de desenvolver, dependiendo de lo que debería suceder en el caso nulo y de la probabilidad de que eso ocurra. Este diseño de lenguaje obliga a que el caso nulo se maneje explícitamente, lo que supongo que mejora la seguridad con respecto a Obj-C (donde es fácil olvidarse de manejar el caso nulo).
Actualizar :
El signo de exclamación también se utiliza en la sintaxis para declarar "Opcionales implícitamente desenvueltos".
En los ejemplos hasta ahora, la john
variable se ha declarado como var john:Person?
y es opcional. Si desea el valor real de esa variable, debe desencapsularla utilizando uno de los tres métodos anteriores.
Si se declarara como var john:Person!
, la variable sería una Opcional implícitamente desenvuelta (consulte la sección con este título en el libro de Apple). No es necesario desenvolver este tipo de variable al acceder al valor y john
se puede utilizar sin sintaxis adicional. Pero el libro de Apple dice:
Las opciones implícitamente desenvueltas no deben usarse cuando existe la posibilidad de que una variable se vuelva nula en un momento posterior. Utilice siempre un tipo opcional normal si necesita comprobar un valor nulo durante la vida útil de una variable.
Actualización 2 :
El artículo " Características interesantes de Swift " de Mike Ash ofrece cierta motivación para los tipos opcionales. Creo que es una escritura genial y clara.
Actualización 3 :
Otro artículo útil sobre el uso opcional implícitamente desenvuelto del signo de exclamación: " Swift and the Last Mile " de Chris Adamson. El artículo explica que esta es una medida pragmática de Apple utilizada para declarar los tipos utilizados por sus marcos Objective-C que pueden contener cero. Declarar un tipo como opcional (usando ?
) o implícitamente desenvuelto (usando !
) es "una compensación entre seguridad y conveniencia". En los ejemplos dados en el artículo, Apple optó por declarar los tipos como implícitamente desenvueltos, lo que hace que el código de llamada sea más conveniente, pero menos seguro.
Quizás Apple podría revisar sus marcos en el futuro, eliminando la incertidumbre de los parámetros implícitamente no empaquetados ("probablemente nunca nulos") y reemplazándolos con opcionales ("ciertamente podría ser nulo en circunstancias particulares [¡con suerte, documentadas!]") o estándar no -Declaraciones opcionales ("nunca es nula"), basadas en el comportamiento exacto de su código Objective-C.
Esto es lo que creo que es la diferencia:
var john: Person?
Significa que John puede ser nulo.
john?.apartment = number73
El compilador interpretará esta línea como:
if john != nil {
john.apartment = number73
}
Mientras
john!.apartment = number73
El compilador interpretará esta línea simplemente:
john.apartment = number73
Por lo tanto, usar !
desenvolverá la declaración if y hará que se ejecute más rápido, pero si john es nulo, se producirá un error de tiempo de ejecución.
Así que envolver aquí no significa que está envuelto en memoria, sino que está envuelto en código, en este caso está envuelto con una declaración if, y debido a que Apple presta mucha atención al rendimiento en tiempo de ejecución, quieren brindarle una manera de Haga que su aplicación se ejecute con el mejor rendimiento posible.
Actualizar:
Volviendo a esta respuesta después de 4 años, ya que obtuve la mayor reputación en Stackoverflow :) Entendí un poco mal el significado de desenvolver en ese momento. Ahora, después de 4 años, creo que el significado de desenvolver aquí es expandir el código desde su forma compacta original. También significa eliminar la vaguedad alrededor de ese objeto, ya que no estamos seguros por definición si es nulo o no. Al igual que la respuesta de Ashley anterior , considérelo como un regalo que no puede contener nada. Pero sigo pensando que desenvolver es desenvolver código y no desenvolver basado en memoria como se usa enum.
TL;DR
¿Qué significa un signo de exclamación en el lenguaje Swift?
El signo de exclamación dice efectivamente: “Sé que esta opción definitivamente tiene un valor; por favor úsalo”. Esto se conoce como desenvolvimiento forzado del valor del opcional:
Ejemplo
let possibleString: String? = "An optional string."
print(possibleString!) // requires an exclamation mark to access its value
// prints "An optional string."
let assumedString: String! = "An implicitly unwrapped optional string."
print(assumedString) // no exclamation mark is needed to access its value
// prints "An implicitly unwrapped optional string."
Fuente: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399
Si john fuera una var opcional (declarada así)
var john: Person?
entonces sería posible que John no tuviera valor (en el lenguaje ObjC, valor nulo)
El signo de exclamación básicamente le dice al compilador "Sé que esto tiene un valor, no es necesario que lo pruebes". Si no quisiera usarlo, podría probarlo condicionalmente:
if let otherPerson = john {
otherPerson.apartment = number73
}
El interior de esto solo evaluará si john tiene un valor.