¿Cuál es la diferencia entre String? y String! (dos formas de crear una variable opcional)?

En The Swift Programming Language (libro de Apple), he leído que puede crear variables opcionales de dos maneras: usando un signo de interrogación (?) O usando un signo de exclamación (!).

La diferencia es que cuando obtienes el valor de una opción con (?) Tienes que usar un signo de exclamación cada vez que quieres el valor:

var str: String? = "Question mark?" println(str!) // Exclamation mark needed str = nil 

Mientras que con un (!) Puedes obtenerlo sin un sufijo:

 var str: String! = "Exclamation mark!" println(str) // No suffix needed str = nil 

¿Cuál es la diferencia y por qué hay 2 formas si no hay ninguna diferencia?

El beneficio real de usar opcionales implícitamente desempaquetados (declarado con!) Está relacionado con la inicialización de clase cuando dos clases se apuntan entre sí y necesita evitar un ciclo de referencia fuerte. Por ejemplo:

Clase A <-> Clase B

La rutina de inicio de la Clase A necesita crear (y poseer) la clase B, y B necesita una referencia débil de regreso a A:

 class A { let instanceOfB: B! init() { self.instanceOfB = B(instanceOfA: self) } } class B { unowned let instanceOfA: A init(instanceOfA: A) { self.instanceOfA = instanceOfA } } 

Ahora,

  • La clase B necesita una referencia a la clase A para ser inicializada.
  • La clase A solo puede pasar al inicializador de la clase B una vez que está completamente inicializada.
  • Para que la clase A se considere inicializada antes de que se cree la clase B, la propiedad instanceOfB debe ser opcional.

Sin embargo, una vez que se ha creado A, ¡sería molesto tener que acceder a instanceOfB usando instanceOfB! ya que sabemos que tiene que haber un B

Para evitar esto, instanceOfB se declara como una implicidad desenvuelta opcional (instanceOfB!), Y podemos acceder utilizando solo instanceOfB. (Además, sospecho que el comstackdor también puede optimizar el acceso de manera diferente).

Un ejemplo de esto se da en las páginas 464 a 466 del libro.

Resumen:

  • Utilizar ? si el valor puede volverse nulo en el futuro, para que lo pruebes.
  • Utilizar ! si realmente no debería volverse nulo en el futuro, pero debe ser nulo inicialmente.

Deberías ir más allá del azúcar sintáctico.

Hay dos tipos polimórficos completamente diferentes. El azúcar sintáctico solo usa uno u otro de estos tipos.

Cuando escribes Foo? como tipo, realmente tienes Optional , ¡cuando escribes Foo! realmente tienes ImplicitlyUnwrappedOptional .

Estos son dos tipos diferentes, y son diferentes de Foo también.

Los valores que creas ? son valores opcionales simples como usted mencionó, debe acceder a ellos a través de un enlace opcional ( if let unwrappedValue = myOptionalValue ) o usando la syntax del punto de exclamación myOptionalValue!.doSomething() .

¡Los valores que creas ! se llaman opcionales implícitamente desempaquetados. Con ellos, no es necesario desenvolver manualmente antes de usarlos. Cuando haces val myOptionalValue!.doSomething() .

El valor se myOptionalValue automáticamente cuando use myOptionalValue directamente, tenga cuidado con esto, porque acceder a un valor implícitamente desenvuelto cuando en realidad no tiene ningún valor (cuando es nil ) dará como resultado un error de tiempo de ejecución.

? (Opcional) indica que su variable puede contener un valor nulo mientras
! (unwrapper) indica que su variable debe tener una memoria (o valor) cuando se usa (intentado obtener un valor de ella) en tiempo de ejecución.

La principal diferencia es que el encadenamiento opcional falla con gracia cuando el opcional es nulo, mientras que el desenvolvimiento forzado desencadena un error de tiempo de ejecución cuando el opcional es nulo.

Para reflejar el hecho de que se puede invocar el encadenamiento opcional en un valor nulo, el resultado de una llamada de encadenamiento opcional siempre es un valor opcional, incluso si la propiedad, el método o el subíndice que está consultando devuelve un valor no opcional. Puede usar este valor de retorno opcional para verificar si la llamada de encadenamiento opcional fue exitosa (la opción opcional devuelta contiene un valor), o no tuvo éxito debido a un valor nulo en la cadena (el valor opcional devuelto es nulo).

Específicamente, el resultado de una llamada de encadenamiento opcional es del mismo tipo que el valor de retorno esperado, pero se envuelve en una opción. Una propiedad que normalmente devuelve un Int devolverá un Int? cuando se accede a través de un encadenamiento opcional.

 var defaultNil : String? // declared variable with default nil value println(defaultNil) >> nil var canBeNil : String? = "test" println(canBeNil) >> optional(test) canBeNil = nil println(canBeNil) >> nil println(canBeNil!) >> // Here nil optional variable is being unwrapped using ! mark (symbol), that will show runtime error. Because a nil optional is being tried to get value using unwrapper var canNotBeNil : String! = "test" print(canNotBeNil) >> "test" var cantBeNil : String = "test" cantBeNil = nil // can't do this as it's not optional and show a compile time error 

Para obtener más detalles, consulte un documento del Apple Developer Commitee, en detalle

The String! tipo se llama una opción opcional implícitamente desenvuelta :

A veces es claro desde la estructura de un progtwig que un opcional siempre tendrá un valor, después de que ese valor se establezca por primera vez. En estos casos, es útil eliminar la necesidad de verificar y desenvolver el valor de la opción cada vez que se acceda, ya que se puede suponer que tiene un valor todo el tiempo.

Este tipo de opciones se definen como opciones opcionales implícitamente desempaquetadas. Usted escribe una opción opcional implícitamente desenvuelta colocando un signo de exclamación (¡Cadena!) En lugar de un signo de interrogación (¿Cadena?) Después del tipo que desea que sea opcional.

en la sección de encadenamiento opcional encuentras la respuesta:

clase de ejemplo:

 class Person { var residence: Residence? } class Residence { var numberOfRooms = 1 } 

Si intenta acceder a la propiedad numberOfRooms de la residencia de esta persona, colocando un signo de admiración después de la residencia para forzar el desenvolvimiento de su valor, desencadena un error de tiempo de ejecución porque no hay un valor de residencia para desenvolver:

 let roomCount = john.residence!.numberOfRooms // this triggers a runtime error 

El código anterior tiene éxito cuando john.residence tiene un valor no nulo y configurará roomCount a un valor Int que contiene el número apropiado de habitaciones. Sin embargo, este código siempre desencadena un error de tiempo de ejecución cuando la residencia es nula, como se ilustra arriba.

El encadenamiento opcional proporciona una forma alternativa de acceder al valor de numberOfRooms. Para usar el encadenamiento opcional, use un signo de interrogación en lugar del signo de exclamación:

 if let roomCount = john.residence?.numberOfRooms { println("John's residence has \(roomCount) room(s).") } else { println("Unable to retrieve the number of rooms.") } // prints "Unable to retrieve the number of rooms." 

Bien mencionado por @tarmes arriba. Notado otro uso de opcional implícito:

Digamos que tengo un Int opcional:

 let firstInt: Int? = 9 

Y estoy tratando de usar la coincidencia de patrones opcional y usar este Int opcional de esta manera:

 if case let myFirstInt? = firstInt where myFirstInt > 1 { print("Valid") } else { print("Invalid") } 

Tenga en cuenta que estoy utilizando una opción implícita con el parámetro local myFirstInt lo que hace que sea seguro para firstInt condición vinculada con firstInt opcional. Si ahora, hago firstInt como nil , ejecutará otra condición. Si, en cambio, utilizo force- firstInt con firstInt eso llevaría a la falla, algo como esto:

enter image description here