programming - ¿Qué significa un signo de exclamación en el lenguaje Swift?
text swift (22)
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, cuando asignan el apartamento a la persona, usan 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 solo hacer lo siguiente?
john.apartment = number73
Soy muy nuevo en el lenguaje Swift. Sólo trato de bajar lo básico.
ACTUALIZAR:
La gran pieza del rompecabezas que faltaba (que no se menciona directamente en las respuestas, al menos no al momento de escribir esto) es que 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 estaba malinterpretando a esa Person
y Person?
Son tipos completamente separados. Una vez que entendí eso, ¿todo lo demás ?
, !
La locura, y las grandes respuestas a continuación, tenían mucho más sentido.
¿Qué significa "desenvolver la instancia"? ¿Por qué es necesario?
Hasta donde puedo trabajar (esto también es muy nuevo para mí) ...
El término "envuelto" implica que deberíamos pensar en una variable opcional como un regalo envuelto en papel brillante, que podría (¡por desgracia!) 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 contiene un valor ( Some(T)
) o no ( None
).
Si hay un valor, esto se puede obtener "desenvolviendo" la variable (obteniendo la T
de Some(T)
).
¿En qué se diferencia
john!.apartment = number73
dejohn.apartment = number73
? (Parafraseado)
Si escribe el nombre de una variable opcional (por ejemplo, texto john
, sin el !
), Se refiere a la enumeración "ajustada" (Algunos / Ninguno), no al valor en sí (T). Así que john
no es una instancia de Person
y no tiene un miembro del apartment
:
john.apartment
// ''Person?'' does not have a member named ''apartment''
El valor real de la Person
se puede desempaquetar de varias maneras:
- "Desenvolvimiento forzado":
john!
(da el valor dePerson
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 elige una de estas formas de desenvolver, dependiendo de lo que debería suceder en el caso nulo y de la probabilidad de que sea así. Este diseño de lenguaje obliga a que el caso nulo se maneje explícitamente, lo que supongo que mejora la seguridad sobre Obj-C (donde es fácil olvidarse de manejar el caso nulo).
Actualización :
El signo de exclamación también se usa en la sintaxis para declarar "Opcionales implícitamente sin envolver".
En los ejemplos hasta ahora, la variable john
ha sido declarada como var john:Person?
, y es un opcional. Si desea el valor real de esa variable, debe desenvolverlo, utilizando uno de los tres métodos anteriores.
Si fuera declarado como var john:Person!
en cambio, la variable sería una Opcional implícitamente sin envolver (consulte la sección con este título en el libro de Apple). No es necesario desempaquetar este tipo de variable al acceder al valor, y john
puede usarse sin sintaxis adicional. Pero el libro de Apple dice:
Los opcionales implícitamente desenvueltos no deben usarse cuando existe la posibilidad de que una variable se vuelva nula en un momento posterior. Siempre use un tipo opcional normal si necesita verificar 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 genial, clara la escritura.
Actualización 3 :
Otro artículo útil sobre el uso opcional implícitamente sin envolver para el signo de exclamación: " Swift and the Last Mile " por Chris Adamson. El artículo explica que esta es una medida pragmática de Apple utilizada para declarar los tipos utilizados por sus marcos de Objective-C que pueden contener nil. Declarar un tipo como opcional ( ?
usar ?
) O implícitamente sin envolver ( !
usar !
) Es "una compensación entre seguridad y conveniencia". En los ejemplos proporcionados en el artículo, Apple ha decidido declarar los tipos como implícitamente desenvueltos, lo que hace que el código de llamada sea más conveniente, pero menos seguro.
Tal vez Apple podría revisar sus marcos en el futuro, eliminando la incertidumbre de los parámetros implícitamente desenvueltos ("probablemente nunca nulos") y reemplazándolos con opcionales ("ciertamente podría ser nulo en particular [¡espero, documentado!] Circunstancias") o estándar no Declaraciones opcionales ("nunca es nula"), basadas en el comportamiento exacto de su código Objective-C.
Algunas perspectivas generales para agregar a otras respuestas útiles pero más centradas en los detalles:
En Swift, el signo de exclamación aparece en varios contextos:
- Desenvolvimiento forzado:
let name = nameLabel!.text
- Opcionales implícitamente
var logo: UIImageView!
- Casting forzado:
logo.image = thing as! UIImage
logo.image = thing as! UIImage
- Excepciones no manejadas:
try! NSJSONSerialization.JSONObjectWithData(data, [])
try! NSJSONSerialization.JSONObjectWithData(data, [])
Cada uno de estos es un constructo de lenguaje diferente con un significado diferente, pero todos tienen tres cosas importantes en común:
1. Los signos de exclamación evitan los controles de seguridad de compilación de Swift.
Cuando usas !
en Swift, básicamente estás diciendo: "Oye, compilador, sé que crees que podría ocurrir un error aquí, pero sé con total certeza que nunca lo hará".
No todos los códigos válidos encajan en la caja del sistema de tipo de tiempo de compilación de Swift, o la comprobación de tipos estáticos de cualquier idioma, para el caso. Hay situaciones en las que se puede demostrar lógicamente que un error nunca ocurrirá, pero no se lo puede demostrar al compilador . Es por eso que los diseñadores de Swift agregaron estas características en primer lugar.
Sin embargo, siempre que lo uses !
, estás descartando tener una ruta de recuperación para un error, lo que significa que ...
2. Los signos de exclamación son posibles choques.
Un signo de exclamación también dice: "Hola Swift, estoy tan seguro de que este error nunca puede suceder que es mejor para ti bloquear toda mi aplicación que para codificar una ruta de recuperación para ella".
Esa es una afirmación peligrosa. Puede ser la correcta: en el código de misión crítica donde ha pensado mucho sobre las invariantes de su código, puede ser que la salida falsa sea peor que un fallo.
Sin embargo, cuando veo !
en la naturaleza, rara vez se utiliza tan atentamente. En cambio, con demasiada frecuencia significa, “este valor era opcional y realmente no pensé demasiado en por qué podría ser nulo o cómo manejar adecuadamente esa situación, ¡pero agregando !
Lo compilé ... así que mi código es correcto, ¿verdad?
Cuidado con la arrogancia del signo de exclamación. En lugar…
3. Los puntos de exclamación se utilizan mejor con moderación.
¡Cada uno de estos !
constructos tiene un ?
contraparte que te obliga a lidiar con el error / nil caso:
- Desenvolvimiento condicional:
if let name = nameLabel?.text { ... }
- Opcionales:
var logo: UIImageView?
-
logo.image = thing as? UIImage
condicionales:logo.image = thing as? UIImage
logo.image = thing as? UIImage
- Excepciones nulas en caso de fallo:
try? NSJSONSerialization.JSONObjectWithData(data, [])
try? NSJSONSerialization.JSONObjectWithData(data, [])
Si estás tentado a usar !
, siempre es bueno considerar cuidadosamente por qué no está utilizando ?
en lugar. Está fallando tu programa realmente la mejor opción si el !
la operación falla? ¿Por qué es ese valor opcional / failable?
¿Existe una ruta de recuperación razonable que su código podría tomar en el caso de error nulo? Si es así, codifícalo.
Si no puede ser nulo, si el error nunca puede ocurrir, ¿hay una manera razonable de volver a trabajar tu lógica para que el compilador sepa eso? Si es así, hazlo; Su código será menos propenso a errores.
Hay ocasiones en las que no hay una forma razonable de manejar un error, y simplemente ignorar el error y, por lo tanto, proceder con datos erróneos, sería peor que estrellarse. Esos son los tiempos para usar fuerza desenvolviendo.
¡Busco periódicamente todo mi código para !
y auditar cada uso de la misma. Muy pocos usos resisten el escrutinio. (A partir de este escrito, todo el marco de Siesta tiene exactamente two instances de él).
Eso no quiere decir que nunca se debe usar !
en su código, solo debe usarlo de manera consciente y nunca convertirlo en la opción predeterminada.
Aquí hay unos ejemplos:
var name:String = "Hello World"
var word:String?
Donde la word
es un valor opcional. significa que puede o no puede contener un valor.
word = name
Aquí el name
tiene un valor para que podamos asignarlo.
var cow:String = nil
var dog:String!
Cuando el dog
se desenvuelve a la fuerza significa que debe contener un valor.
dog = cow
La aplicación se bloqueará porque estamos asignando nil
a desenvuelto
En corto (!): Después de declarar una variable y de que está seguro de que la variable mantiene un valor.
let assumedString: String! = "Some message..."
let implicitString: String = assumedString
de lo contrario tendrías que hacer esto en cada valor posterior ...
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark
En el objetivo C, las variables sin valor eran iguales a ''nil'' (también era posible usar valores ''nil'' igual a 0 y falso), por lo tanto, era posible usar variables en sentencias condicionales (las variables que tienen valores son iguales a ''TRUE ''y aquellos sin valores fueron iguales a'' FALSO '').
Swift proporciona seguridad de tipo proporcionando "valor opcional". Es decir, evita que se formen errores al asignar variables de diferentes tipos.
Entonces, en Swift, solo los booleanos pueden proporcionarse en sentencias condicionales.
var hw = "Hello World"
Aquí, aunque ''hw'' es una cadena, no se puede usar en una sentencia if como en el objetivo C.
//This is an error
if hw
{..}
Para eso necesita ser creado como,
var nhw : String? = "Hello World"
//This is correct
if nhw
{..}
En este caso...
var John: ¡Persona!
significa que, inicialmente, John tendrá un valor nulo, se establecerá y, una vez establecido, nunca volverá a ser cero. Por lo tanto, por conveniencia, puedo usar la sintaxis más sencilla para acceder a una var opcional porque esta es una "Opcional implícitamente desenvuelta"
En pocas palabras, los signos de exclamación significan que se está desempaquetando un opcional. Una opcional es una variable que puede tener un valor o no, por lo que puede verificar si la variable está vacía, usando una instrucción if como se muestra aquí , y luego forzarla a desenvolverla. Sin embargo, si fuerza el desenvolvimiento de un opcional que está vacío, su programa se bloqueará, así que tenga cuidado. Las opciones se declaran poniendo un signo de interrogación al final de una asignación explícita a una variable, por ejemplo, podría escribir:
var optionalExample: String?
Esta variable no tiene valor. Si tuviera que desenvolverlo, el programa fallaría y Xcode le diría que intentó desenvolver un opcional con un valor de nulo.
Espero que haya ayudado.
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 como simplemente:
john.apartment = number73
Por lo tanto, utilizando! Desenvolverá la instrucción if y la ejecutará más rápido, pero si John es nulo, se producirá un error en 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 darle una forma de Haz que tu aplicación se ejecute con el mejor rendimiento posible.
Actualizar:
Volviendo a esta respuesta después de 4 años, ya que obtuve la reputación más alta en :) No entendí un poco 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 de su forma compacta original. También significa eliminar la vaguedad alrededor de ese objeto, ya que no estamos seguros, por definición, de que sea nulo o no. Al igual que la respuesta de Ashley, piénsalo como un regalo que no podría contener nada. Pero sigo pensando que el desenvolvimiento es un desenvolvimiento de código y no un desenvolvimiento basado en memoria como el uso de enumeración.
John es una Persona opcional, lo que significa que puede mantener un valor o ser nulo.
john.apartment = number73
se usa si john no es un opcional. Como John nunca es nulo, podemos estar seguros de que no llamará apartamento a un valor nulo. Mientras
john!.apartment = number73
promete al compilador que john no es nulo, luego desenvuelve el opcional para obtener el valor de john y accede a la propiedad del departamento de john. Use esto si sabe que John no es nulo. Si lo llama en una opción nula, obtendrá un error de tiempo de ejecución.
La documentación incluye un buen ejemplo para usar esto donde convertNumber es opcional.
if convertedNumber {
println("/(possibleNumber) has an integer value of /(convertedNumber!)")
} else {
println("/(possibleNumber) could not be converted to an integer")
}
Los ! al final de un objeto, dice que el objeto es opcional y que se debe desempaquetar si puede devolver un valor nulo. Esto se usa a menudo para atrapar errores que de otra manera bloquearían el programa.
Los ! significa que eres fuerza desenvolviendo el objeto el! sigue Puede encontrar más información en la documentación de Apple, que se puede encontrar aquí: https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/TheBasics.html
Si John fuera una var opcional (declarada así)
var john: Person?
entonces sería posible que John no tuviera ningún valor (en el lenguaje de ObjC, valor nulo)
El signo de exclamación básicamente le dice al compilador "Sé que esto tiene un valor, no es necesario probarlo". Si no quería usarlo, podría probarlo condicionalmente:
if let otherPerson = john {
otherPerson.apartment = number73
}
El interior de esto solo evaluará si John tiene un valor.
Si está familiarizado con C #, esto es como los tipos de Nullable que también se declaran usando un signo de interrogación:
Person? thisPerson;
Y el signo de exclamación en este caso es equivalente a acceder a la propiedad .Value del tipo anulable como este:
thisPerson.Value
Si lo usa como opcional, desenvuelve el opcional y ve si hay algo allí. Si lo usa en una sentencia if-else es el código para NOT. Por ejemplo,
if (myNumber != 3){
// if myNumber is NOT 3 do whatever is inside these brackets.
)
Si proviene de un lenguaje de la familia C, estará pensando en "puntero a objeto del tipo X, que podría ser la dirección de memoria 0 (NULL)", y si proviene de un lenguaje de tipo dinámico, será pensamiento "Objeto que es probablemente de tipo X pero podría ser de tipo indefinido". Ninguno de estos es realmente correcto, aunque de una manera indirecta el primero está cerca.
La forma en que deberías pensar en ello es como si fuera un objeto como:
struct Optional<T> {
var isNil:Boolean
var realObject:T
}
Cuando estás probando tu valor opcional con foo == nil
, en realidad está devolviendo foo.isNil
, y cuando dices foo!
está devolviendo foo.realObject
con una afirmación de que foo.isNil == false
. Es importante tener en cuenta esto porque si foo
realidad es nulo cuando lo haces foo!
, eso es un error de tiempo de ejecución, por lo que normalmente querría usar un permiso condicional a menos que esté muy seguro de que el valor no será nulo. Este tipo de truco significa que el lenguaje puede ser tipificado fuertemente sin obligarlo a probar si los valores son nulos en todas partes.
En la práctica, realmente no se comporta así porque el compilador hace el trabajo. En un nivel alto hay un tipo Foo?
que es independiente de Foo
, y que impide que las funciones que aceptan el tipo Foo
reciban un valor nulo, pero en un nivel bajo, un valor opcional no es un objeto verdadero porque no tiene propiedades ni métodos; es probable que, de hecho, sea un puntero que puede ser NULL (0) con la prueba apropiada al forzar el desenvolvimiento.
Hay otra situación en la que verías un signo de exclamación en un tipo, como en:
func foo(bar: String!) {
print(bar)
}
Esto es aproximadamente equivalente a aceptar un opcional con un desenvolvimiento forzado, es decir:
func foo(bar: String?) {
print(bar!)
}
Puede usar esto para tener un método que técnicamente acepte un valor opcional pero tendrá un error de tiempo de ejecución si es nulo. En la versión actual de Swift, esto aparentemente pasa por alto la aserción is-not-nil, por lo que tendrá un error de bajo nivel en su lugar. Generalmente no es una buena idea, pero puede ser útil al convertir código de otro idioma.
TL; DR
¿Qué significa un signo de exclamación en el lenguaje Swift?
El signo de exclamación efectivamente dice: “Sé que este opcional definitivamente tiene un valor; por favor úselo ”. Esto se conoce como desempaquetado 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."
Toda la historia comienza con una característica de swift llamada vars opcionales. Estas son las variables que pueden tener un valor o pueden no tener un valor. En general, swift no nos permite usar una variable que no esté inicializada, ya que esto puede provocar fallos o razones inesperadas, y también puede ser un marcador de posición para las puertas traseras. Por lo tanto, para declarar una variable cuyo valor no está determinado inicialmente, usamos un ''?''. Cuando se declara una variable de este tipo, para usarla como parte de alguna expresión hay que desenvolverla antes de usarla, desenvolver es una operación mediante la cual se descubre el valor de una variable, esto se aplica a los objetos. Sin desempaquetar, si intenta usarlos, tendrá un error de tiempo de compilación. Para desenvolver una variable que es una variable opcional, el signo de exclamación "!" se utiliza
Ahora hay ocasiones en las que sabe que a tales variables opcionales se les asignarán valores por ejemplo, por sistema o por su propio programa, pero más adelante, por ejemplo, salidas de IU, en tal situación en lugar de declarar una variable opcional con un signo de interrogación "?" usamos "!".
Así el sistema sabe que esta variable que se declara con "!" es opcional en este momento y no tiene ningún valor, pero recibirá un valor más adelante en su vida útil.
Por lo tanto, el signo de exclamación tiene dos usos diferentes, 1. Para declarar una variable que será opcional y recibirá un valor definitivamente más tarde 2. Para desenvolver una variable opcional antes de usarla en una expresión.
Las descripciones anteriores evitan demasiadas cosas técnicas, espero.
Una variable opcional puede contener un valor o no puede
caso 1: var myVar:String? = "Something"
var myVar:String? = "Something"
caso 2: var myVar:String? = nil
var myVar:String? = nil
¡ahora si le preguntas a myVar !, le estás diciendo a un compilador que devuelva un valor en el caso 1, devolverá "Something"
En el caso 2 se estrellará.
Sentido ! mark forzará al compilador a devolver un valor, incluso si no está allí. Es por eso que el nombre Force Unwrapping .
john
es una var
opcional Así puede ser contiene un valor nil
. Para asegurarse de que el valor no sea nulo, use un !
al final del nombre var
.
De la documentación
“Una vez que esté seguro de que el opcional contiene un valor, puede acceder a su valor subyacente agregando un signo de exclamación (!) Al final del nombre del opcional. El signo de exclamación efectivamente dice: “Sé que este opcional definitivamente tiene un valor; por favor utilízalo ".
Otra forma de comprobar el valor no nulo es
if let j = json {
// do something with j
}
En palabras sencillas
USAR Signo de exclamación indica que la variable debe tener un valor no nulo (nunca será nulo)
PREGÚNTESE
- ¿El tipo de
person?
¿Tiene un miembro deapartment
/ propiedad? O - ¿La
person
tipo tiene un miembro deapartment
/ propiedad?
Si no puedes responder esta pregunta, entonces continúa leyendo:
Para comprender, es posible que necesite un nivel superbásico de comprensión de los genéricos . Ver here Muchas cosas en Swift están escritas usando Genéricos. Opcionales incluidos
El código de abajo está disponible en este video de Stanford . Recomiendo encarecidamente que veas los primeros 5 minutos.
Una Opcional es una enumeración con solo 2 casos.
enum Optional<T>{
case None
case Some(T)
}
let x: String? = nil //actually means:
let x = Optional<String>.None
let x :String? = "hello" //actually means:
let x = Optional<String>.Some("hello")
var y = x! // actually means:
switch x {
case .Some(let value): y = value
case .None: // Raise an exception
}
Encuadernación opcional:
let x:String? = something
if let y = x {
// do something with y
}
//Actually means:
switch x{
case .Some(let y): print)(y) // or whatever else you like using
case .None: break
}
cuando dices var john: Person?
En realidad te refieres a tal:
enum Optional<Person>{
case .None
case .Some(Person)
}
¿La enumeración anterior tiene alguna propiedad llamada apartment
? ¿Lo ves en alguna parte? ¡No está allí en absoluto! Sin embargo si lo desenvuelven es decir hacer person!
entonces puede ... lo que hace bajo el capó es: Optional<Person>.Some(Person(name: "John Appleseed"))
¿Habías definido var john: Person
lugar de: var john: Person?
entonces ya no habría necesitado tener el !
utilizado, porque la Person
sí tiene un miembro del apartment
Como una discusión futura sobre por qué usar !
para desenvolver a veces no se recomienda ver esta P&R
Simple the Optional variable allows nil to be stored.
var str : String? = nil
str = "Data"
To convert Optional to the Specific DataType, We unwrap the variable using the keyword "!"
func get(message : String){
return
}
get(message : str!) // Unwapped to pass as String