the programming language for ciclo swift optional forced-unwrapping

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 de john.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 de Person si existe, error de tiempo de ejecución si es nulo)
  • "enlace opcional": if let p = john { println(p) } (ejecuta println 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 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.



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."

Fuente: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399


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 de apartment / propiedad? O
  • ¿La person tipo tiene un miembro de apartment / 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