errors - swift try else
¿Qué significa "error fatal: inesperadamente encontrado nulo al desenvolver un valor opcional"? (10)
Mi programa Swift se está
EXC_BAD_INSTRUCTION
con
EXC_BAD_INSTRUCTION
y este error.
¿Qué significa y cómo lo soluciono?
error fatal: inesperadamente encontrado nulo al desenvolver un valor opcional
Esta publicación está destinada a recopilar respuestas a problemas "inesperadamente encontrados", para que no estén dispersos y sean difíciles de encontrar. Siéntase libre de agregar su propia respuesta o edit la respuesta wiki existente.
TL; respuesta DR
Con muy pocas excepciones , esta regla es dorada:
Evite el uso de
!
Declarar variable opcional (
?
), No implícitamente opciones opcionales sin envolver (IUO) (
!
)
En otras palabras, más bien use:
var nameOfDaughter: String?
En vez de:
var nameOfDaughter: String!
Desenvuelva la variable opcional usando
if let
o
guard let
Cualquiera de las variables de desenvolver así:
if let nameOfDaughter = nameOfDaughter {
print("My daughters name is: /(nameOfDaughter)")
}
O así:
guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: /(nameOfDaughter)")
Esta respuesta pretendía ser concisa, para una comprensión completa, lea la respuesta aceptada
Básicamente, trataste de usar un valor nulo en lugares donde Swift solo permite valores nulos, diciéndole al compilador que confíe en ti que nunca habrá un valor nulo allí, permitiendo así que tu aplicación se compile.
Hay varios escenarios que conducen a este tipo de error fatal:
-
desenvolturas forzadas:
let user = someVariable!
Si
someVariable
es nulo, obtendrá un bloqueo. Al realizar un desenvolvimiento forzado, usted transfirió la responsabilidad de verificación nula del compilador a usted, básicamente al realizar un desenvolvimiento forzado, le garantiza al compilador que nunca tendrá valores nulos allí. ¿Y adivina qué sucede si de alguna manera un valor nulo termina ensomeVariable
?¿Solución? Utilice el enlace opcional (también conocido como if-let), realice el procesamiento de variables allí:
if user = someVariable { // do your stuff }
-
moldes forzados (abajo):
let myRectangle = someShape as! Rectangle
Aquí, al forzar el lanzamiento, le dice al compilador que ya no se preocupe, ya que siempre tendrá una instancia de
Rectangle
allí. Y mientras eso se mantenga, no tiene que preocuparse. Los problemas comienzan cuando usted o sus colegas del proyecto comienzan a circular valores no rectangulares.¿Solución? Utilice el enlace opcional (también conocido como if-let), realice el procesamiento de variables allí:
if let myRectangle = someShape as? Rectangle { // yay, I have a rectangle }
-
Opciones implícitamente sin envolver. Supongamos que tiene la siguiente definición de clase:
class User { var name: String! init() { name = "(unnamed)" } func nicerName() { return "Mr/Ms " + name } }
Ahora, si nadie se equivoca con la propiedad de
name
configurándola comonil
, entonces funciona como se esperaba, sin embargo, si elUser
se inicializa desde un JSON que carece de la clave dename
, entonces obtiene el error fatal al intentar usar la propiedad .¿Solución? No los use :) A menos que esté 102% seguro de que la propiedad siempre tendrá un valor no nulo para el momento en que deba usarse. En la mayoría de los casos, la conversión a opcional o no opcional funcionará. Hacerlo no opcional también dará como resultado que el compilador lo ayude diciéndole las rutas de código que omitió dando un valor a esa propiedad
-
Tomas de corriente no conectadas o todavía no conectadas. Este es un caso particular del escenario # 3. Básicamente tiene alguna clase cargada con XIB que desea usar.
class SignInViewController: UIViewController { @IBOutlet var emailTextField: UITextField! }
Ahora, si no conectó la salida desde el editor XIB, la aplicación se bloqueará tan pronto como quiera usar la salida. ¿Solución? Asegúrese de que todos los enchufes estén conectados. O usar el
?
operador en ellos:emailTextField?.text = "[email protected]"
. O declare la salida como opcional, aunque en este caso el compilador lo obligará a desenvolverlo en todo el código. -
Valores provenientes de Objective-C, y que no tienen anotaciones de nulabilidad. Supongamos que tenemos la siguiente clase Objective-C:
@interface MyUser: NSObject @property NSString *name; @end
Ahora, si no se especifican anotaciones de nulabilidad (ya sea explícitamente o mediante
NS_ASSUME_NONNULL_BEGIN
/NS_ASSUME_NONNULL_END
), ¡la propiedad delname
se importará en Swift asString!
(un IUO - opcional sin envolver implícitamente). Tan pronto como un código rápido quiera usar el valor, se bloqueará si elname
es nulo.¿Solución? Agregue anotaciones de nulabilidad a su código Objective-C. Sin embargo, tenga en cuenta que el compilador Objective-C es un poco permisivo en lo que respecta a la anulabilidad, puede terminar con valores nulos, incluso si los marcó explícitamente como no
nonnull
.
Dado que las respuestas anteriores explican claramente cómo jugar de forma segura con Opcionales. Trataré de explicar qué opciones son realmente rápidas.
Otra forma de declarar una variable opcional es
var i : Optional<Int>
Y el tipo opcional no es más que una enumeración con dos casos, es decir
enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
.
.
.
}
Entonces, para asignar un nulo a nuestra variable ''i''.
Podemos hacer
var i = Optional<Int>.none
o para asignar un valor, pasaremos algún valor
var i = Optional<Int>.some(28)
Según swift, ''nulo'' es la ausencia de valor.
Y para crear una instancia inicializada con
nil
Tenemos que cumplir con un protocolo llamado
ExpressibleByNilLiteral
y excelente si lo adivinó, solo los
Optionals
ajustan a
ExpressibleByNilLiteral
y se desaconseja la conformidad con otros tipos.
ExpressibleByNilLiteral
tiene un único método llamado
init(nilLiteral:)
que inicializa una instancia con nil.
Por lo general, no llamará a este método y, según la documentación rápida, se desaconseja llamar a este inicializador directamente, ya que el compilador lo llama cada vez que inicializa un tipo Opcional con literal
nil
.
Incluso yo mismo tengo que envolver (sin juego de palabras) mi cabeza. Opcionales: D Happy Swfting All .
Esta pregunta surge TODO EL TIEMPO en SO. Es una de las primeras cosas con las que luchan los nuevos desarrolladores de Swift.
Fondo:
Swift utiliza el concepto de "Opcionales" para tratar con valores que pueden contener un valor, o no. En otros lenguajes como C, puede almacenar un valor de 0 en una variable para indicar que no contiene ningún valor. Sin embargo, ¿qué pasa si 0 es un valor válido? Entonces podrías usar -1. ¿Qué pasa si -1 es un valor válido? Y así.
Las opciones rápidas le permiten configurar una variable de cualquier tipo para contener un valor válido o ningún valor.
Pones un signo de interrogación después del tipo cuando declaras que una variable significa (tipo x, o ningún valor).
Un opcional es en realidad un contenedor que contiene una variable de un tipo dado o nada.
Una opción debe ser "desenvuelta" para obtener el valor dentro.
Los "!" El operador es un operador de "desenvolvimiento forzado". Dice "confía en mí. Sé lo que estoy haciendo. Te garantizo que cuando se ejecute este código, la variable no contendrá nada". Si te equivocas, te estrellas.
A menos que realmente sepa lo que está haciendo, evite el "!" forzar al operador de desenvolver. Es probablemente la mayor fuente de accidentes para los programadores principiantes de Swift.
Cómo lidiar con las opciones:
Hay muchas otras formas de tratar con opciones que son más seguras. Aquí hay algunos (no una lista exhaustiva)
Puede usar "enlace opcional" o "if let" para decir "si este opcional contiene un valor, guarde ese valor en una nueva variable no opcional. Si el opcional no contiene un valor, omita el cuerpo de esta instrucción if ".
Aquí hay un ejemplo de enlace opcional con nuestro
foo
opcional:
if let newFoo = foo //If let is called optional binding. {
print("foo is not nil")
} else {
print("foo is nil")
}
Tenga en cuenta que la variable que define cuando utiliza la opción de oferta solo existe (solo está "dentro del alcance") en el cuerpo de la instrucción if.
Alternativamente, puede usar una declaración de protección, que le permite salir de su función si la variable es nula:
func aFunc(foo: Int?) {
guard let newFoo = input else { return }
//For the rest of the function newFoo is a non-optional var
}
Las declaraciones de Guardia se agregaron en Swift 2. Guard le permite preservar el "camino dorado" a través de su código y evitar niveles cada vez mayores de ifs anidados que a veces resultan del uso del enlace opcional "if let".
También hay una construcción llamada "operador de fusión nula". Toma la forma "optional_var ?? replace_val". Devuelve una variable no opcional con el mismo tipo que los datos contenidos en el opcional. Si el opcional contiene nil, devuelve el valor de la expresión después de "??" símbolo.
Entonces podrías usar un código como este:
let newFoo = foo ?? "nil" // "??" is the nil coalescing operator
print("foo = /(newFoo)")
También puede usar el manejo de errores try / catch o guard, pero generalmente una de las otras técnicas anteriores es más limpia.
EDITAR:
Otro gotcha un poco más sutil con opcionales son los "opcionales implícitamente sin envolver. Cuando declaramos foo, podríamos decir:
var foo: String!
En ese caso, foo sigue siendo opcional, pero no tiene que desenvolverlo para referenciarlo. Eso significa que cada vez que intentas hacer referencia a foo, te cuelgas si es nulo.
Entonces este código:
var foo: String!
let upperFoo = foo.capitalizedString
Se bloqueará en referencia a la propiedad CapitalizedString de foo a pesar de que no estamos desenvolviendo forzosamente a foo. la impresión se ve bien, pero no lo es.
Por lo tanto, debe tener mucho cuidado con las opciones implícitamente sin envolver. (y tal vez incluso evitarlos por completo hasta que tenga una sólida comprensión de las opciones).
En pocas palabras: cuando esté aprendiendo Swift por primera vez, simule el "!" El carácter no es parte del lenguaje. Es probable que te meta en problemas.
Este es un comentario más importante y por eso las opciones implícitamente desenvueltas pueden ser engañosas cuando se trata de depurar valores
nil
.
Piense en el siguiente código: Se compila sin errores / advertencias:
c1.address.city = c3.address.city
Sin embargo, en tiempo de ejecución da el siguiente error: Error fatal: se encontró inesperadamente nulo al desenvolver un valor opcional
¿Me puede decir qué objeto es
nil
?
No puedes!
El código completo sería:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var c1 = NormalContact()
let c3 = BadContact()
c1.address.city = c3.address.city // compiler hides the truth from you and then you sudden get a crash
}
}
struct NormalContact {
var address : Address = Address(city: "defaultCity")
}
struct BadContact {
var address : Address!
}
struct Address {
var city : String
}
Larga historia corta usando la
var address : Address!
estás
ocultando
la posibilidad de que una variable pueda ser
nil
de otros lectores.
Y cuando se bloquea, dices "¡¿Qué demonios ?! ¡Mi
address
no es opcional, así que, ¿por qué me estrello ?!
Por lo tanto, es mejor escribir como tal:
c1.address.city = c2.address!.city // ERROR: Fatal error: Unexpectedly found nil while unwrapping an Optional value
¿Puedes decirme qué objeto es que fue
nil
?
Esta vez, el código se te ha dejado más claro.
Puede racionalizar y pensar que es probable que sea el parámetro de
address
que se desenvolvió con fuerza.
El código completo sería:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var c1 = NormalContact()
let c2 = GoodContact()
c1.address.city = c2.address!.city
c1.address.city = c2.address?.city // not compile-able. No deceiving by the compiler
c1.address.city = c2.address.city // not compile-able. No deceiving by the compiler
if let city = c2.address?.city { // safest approach. But that''s not what I''m talking about here.
c1.address.city = city
}
}
}
struct NormalContact {
var address : Address = Address(city: "defaultCity")
}
struct GoodContact {
var address : Address?
}
struct Address {
var city : String
}
Los errores
EXC_BAD_INSTRUCTION
y
fatal error: unexpectedly found nil while unwrapping an Optional value
aparece más cuando se declara un
@IBOutlet
, pero no está conectado al
guión gráfico
.
También debe aprender sobre cómo funcionan los opcionales , mencionados en otras respuestas, pero esta es la única vez que me parece más importante.
Primero, debe saber qué es un valor opcional. Puede pasar al Lanzamiento de programación rápido
para detalles
En segundo lugar, debe saber que el valor opcional tiene dos estados. Uno es el valor completo, y el otro es el valor nulo. Entonces, antes de implementar un valor opcional, debe verificar qué estado es.
Puede usar
if let ...
o
guard let ... else
y así sucesivamente.
Otra forma, si no desea verificar su estado antes de su implementación, también puede usar
var buildingName = buildingName ?? "buildingName"
var buildingName = buildingName ?? "buildingName"
lugar.
Tuve este error una vez cuando intentaba establecer mis valores de Outlets desde el método de preparación para segue de la siguiente manera:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DestinationVC{
if let item = sender as? DataItem{
// This line pops up the error
destination.nameLabel.text = item.name
}
}
}
Luego descubrí que no puedo establecer los valores de las salidas del controlador de destino porque el controlador aún no se ha cargado o inicializado.
Entonces lo resolví de esta manera:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DestinationVC{
if let item = sender as? DataItem{
// Created this method in the destination Controller to update its outlets after it''s being initialized and loaded
destination.updateView(itemData: item)
}
}
}
Controlador de destino:
// This variable to hold the data received to update the Label text after the VIEW DID LOAD
var name = ""
// Outlets
@IBOutlet weak var nameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
nameLabel.text = name
}
func updateView(itemDate: ObjectModel) {
name = itemDate.name
}
Espero que esta respuesta ayude a cualquiera con el mismo problema, ya que encontré que la respuesta marcada es un gran recurso para comprender las opciones y cómo funcionan, pero no ha abordado el problema directamente.
Swift 5 y superior ( opcional y encadenamiento opcional )
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if let buildingNumber = buildingNumber, let street = street {
return "/(buildingNumber) /(street)"
} else if buildingName != nil {
return buildingName
} else {
return nil
}
}
}
Esta respuesta es wiki comunitaria . Si crees que podría mejorarse, ¡puedes edit !
Antecedentes: ¿Qué es un opcional?
En Swift,
Optional
es un
tipo genérico
que puede contener un valor (de cualquier tipo), o ningún valor en absoluto.
En muchos otros lenguajes de programación, a menudo se usa
un valor
"centinela" particular para indicar la
falta de un valor
.
En Objective-C, por ejemplo,
nil
(el
puntero nulo
) indica la falta de un objeto.
Pero esto se vuelve más complicado cuando se trabaja con tipos primitivos: ¿debería usarse
-1
para indicar la ausencia de un número entero, o tal vez
INT_MIN
, o algún otro número entero?
Si se elige cualquier valor particular para que signifique "sin número entero", eso significa que ya no puede tratarse como un valor
válido
.
Swift es un lenguaje de tipo seguro, lo que significa que el lenguaje le ayuda a ser claro acerca de los tipos de valores con los que puede trabajar su código. Si parte de su código espera una Cadena, el tipo de seguridad le impide pasar un Int por error.
En Swift,
cualquier tipo
puede hacerse opcional
.
Un valor opcional puede tomar cualquier valor del tipo original
or
el valor especial
nil
.
Los opcionales se definen con un
?
sufijo en el tipo:
var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int? // `nil` is the default when no value is provided
La falta de un valor en un opcional se indica por
nil
:
anOptionalInt = nil
(Tenga en cuenta que este valor
nil
no es igual al
nil
en Objective-C. En Objective-C,
nil
es la ausencia de un
puntero de objeto
válido; en Swift, los opcionales no están restringidos a objetos / tipos de referencia. Opcional se comporta de manera similar a Haskell
Maybe
)
¿Por qué recibí " error fatal: inesperadamente encontrado nulo al desenvolver un valor opcional "?
Para acceder al valor de un opcional (si tiene uno), debe desenvolverlo . Un valor opcional se puede desenvolver de forma segura o forzada. Si desenvuelve a la fuerza un opcional, y no tenía un valor, su programa se bloqueará con el mensaje anterior.
Xcode le mostrará el bloqueo resaltando una línea de código. El problema ocurre en esta línea.
Este bloqueo puede ocurrir con dos tipos diferentes de desenrollado forzado:
1. Desenvolvimiento de fuerza explícito
Esto se hace con el
!
operador de forma opcional.
Por ejemplo:
let anOptionalString: String?
print(anOptionalString!) // <- CRASH
Como
anOptionalString
es
nil
aquí, obtendrá un bloqueo en la línea donde fuerza desenvolverlo.
2. Opciones opcionales implícitamente sin envolver
Estos se definen con un
!
, en lugar de un
?
después del tipo.
var optionalDouble: Double! // this value is implicitly unwrapped wherever it''s used
Se supone que estos opcionales contienen un valor. Por lo tanto, siempre que acceda a una opción sin envoltura implícita, automáticamente se desenvolverá forzosamente para usted. Si no contiene un valor, se bloqueará.
print(optionalDouble) // <- CRASH
Para determinar qué variable causó el bloqueo, puede mantener presionada ⌥ mientras hace clic para mostrar la definición, donde puede encontrar el tipo opcional.
Los IBOutlets, en particular, generalmente son opciones implícitas sin envolver.
Esto se debe a que su xib o storyboard vincularán las salidas en tiempo de ejecución,
después de la
inicialización.
Por lo tanto, debe asegurarse de que no está accediendo a las salidas antes de que se carguen. También debe verificar que las conexiones sean correctas en su archivo storyboard / xib, de lo contrario, los valores serán
nil
en el tiempo de ejecución y, por lo tanto, se bloquearán cuando estén implícitamente sin envolver
Al arreglar las conexiones, intente eliminar las líneas de código que definen sus puntos de venta, luego vuelva a conectarlos.
¿Cuándo debería forzar el desenvolvimiento de un Opcional?
Desenvolvimiento de fuerza explícito
Como regla general, nunca debe forzar explícitamente el desenvolvimiento de un opcional con el
!
operador.
¡Puede haber casos en donde usar
!
es aceptable, pero solo debería usarlo si está 100% seguro de que lo opcional contiene un valor.
Si bien puede haber una ocasión en la que pueda usar el desenvolvimiento forzado, como sabe con certeza que un opcional contiene un valor, no hay un solo lugar donde no pueda desenvolver ese opcional de manera segura.
Opciones implícitas sin envolver
Estas variables están diseñadas para que pueda diferir su asignación hasta más adelante en su código. Es su responsabilidad asegurarse de que tengan un valor antes de acceder a ellos. Sin embargo, debido a que implican el desenvolvimiento forzado, siguen siendo inherentemente inseguros, ya que suponen que su valor no es nulo, a pesar de que la asignación nula es válida.
Solo debe usar opciones opcionales implícitamente sin envolver como último recurso . Si puede utilizar una variable diferida o proporcionar un valor predeterminado para una variable, debe hacerlo en lugar de utilizar una opción implícita sin envolver.
Sin embargo, hay algunos escenarios en los que las opciones opcionales sin envoltura son beneficiosas , y aún puede usar varias formas de desenvolverlas de manera segura como se enumera a continuación, pero siempre debe usarlas con la debida precaución.
¿Cómo puedo tratar con seguridad los opcionales?
La forma más sencilla de verificar si un opcional contiene un valor, es compararlo con
nil
.
if anOptionalInt != nil {
print("Contains a value!")
} else {
print("Doesn’t contain a value.")
}
Sin embargo, el 99.9% del tiempo cuando trabaje con opciones, en realidad querrá acceder al valor que contiene, si es que contiene uno. Para hacer esto, puede usar Enlace opcional .
Enlace opcional
El enlace opcional le permite verificar si un opcional contiene un valor, y le permite asignar el valor sin envolver a una nueva variable o constante.
Utiliza la sintaxis
if let x = anOptional {...}
o
if var x = anOptional {...}
, dependiendo de si necesita modificar el valor de la nueva variable después de vincularla.
Por ejemplo:
if let number = anOptionalInt {
print("Contains a value! It is /(number)!")
} else {
print("Doesn’t contain a number")
}
Lo que esto hace es comprobar primero que el opcional contiene un valor.
Si lo
hace
, el valor ''sin envolver'' se asigna a una nueva variable (
number
), que puede usar libremente como si no fuera opcional.
Si el opcional
no
contiene un valor, se invocará la cláusula else, como era de esperar.
Lo bueno de la encuadernación opcional es que puede desenvolver múltiples opcionales al mismo tiempo. Simplemente puede separar las declaraciones con una coma. La declaración tendrá éxito si todas las opciones se desenvolvieran.
var anOptionalInt : Int?
var anOptionalString : String?
if let number = anOptionalInt, let text = anOptionalString {
print("anOptionalInt contains a value: /(number). And so does anOptionalString, it’s: /(text)")
} else {
print("One or more of the optionals don’t contain a value")
}
Otro buen truco es que también puede usar comas para verificar una determinada condición en el valor, después de desenvolverlo.
if let number = anOptionalInt, number > 0 {
print("anOptionalInt contains a value: /(number), and it’s greater than zero!")
}
El único inconveniente con el uso del enlace opcional dentro de una declaración if es que solo puede acceder al valor sin envolver dentro del alcance de la declaración. Si necesita acceder al valor desde fuera del alcance de la declaración, puede usar una declaración de protección .
Una
declaración de protección le
permite definir una condición para el éxito, y el alcance actual solo continuará ejecutándose si se cumple esa condición.
Se definen con la
guard condition else {...}
sintaxis
guard condition else {...}
.
Entonces, para usarlos con un enlace opcional, puede hacer esto:
guard let number = anOptionalInt else {
return
}
(Tenga en cuenta que dentro del cuerpo de guardia, debe usar una de las declaraciones de transferencia de control para salir del alcance del código que se está ejecutando actualmente).
Si
anOptionalInt
contiene un valor, se
anOptionalInt
y se asignará a la nueva constante de
number
.
El código
después
del guardia continuará ejecutándose.
Si no contiene un valor, el guardia ejecutará el código entre paréntesis, lo que conducirá a la transferencia de control, de modo que el código inmediatamente posterior no se ejecutará.
Lo realmente bueno de las declaraciones de guardia es que el valor sin envolver ahora está disponible para usar en el código que sigue a la declaración (ya que sabemos que el código futuro solo puede ejecutarse si el opcional tiene un valor). Esto es ideal para eliminar las ''pirámides de la fatalidad'' creadas al anidar múltiples sentencias if.
Por ejemplo:
guard let number = anOptionalInt else {
return
}
print("anOptionalInt contains a value, and it’s: /(number)!")
Los guardias también admiten los mismos trucos que la declaración if admite, como desenvolver múltiples opciones al mismo tiempo y usar la cláusula
where
.
Si usa una declaración if o guard depende completamente de si algún código futuro requiere que lo opcional contenga un valor.
Operador de fusión nula
El
Operador de fusión nula
es una ingeniosa versión abreviada del
operador condicional ternario
, diseñado principalmente para convertir opcionales en no opcionales.
Tiene la sintaxis
a ?? b
a ?? b
, donde
a
es un tipo opcional
b
es el mismo tipo que
a
(aunque generalmente no es opcional).
Esencialmente le permite decir "Si
a
contiene un valor, desenvuélvalo.
Si no es así, devuelve
b
lugar ".
Por ejemplo, podría usarlo así:
let number = anOptionalInt ?? 0
Esto definirá una constante de
number
de tipo
Int
, que contendrá el valor de
anOptionalInt
, si contiene un valor, o
0
caso contrario.
Es solo una abreviatura de:
let number = anOptionalInt != nil ? anOptionalInt! : 0
Encadenamiento opcional
Puede usar el
encadenamiento opcional
para llamar a un método o acceder a una propiedad en un opcional.
Esto se hace simplemente sufijando el nombre de la variable con un
?
cuando lo usa
Por ejemplo, supongamos que tenemos una variable
foo
, de tipo una instancia opcional de
Foo
.
var foo : Foo?
Si quisiéramos llamar a un método en
foo
que no devuelve nada, simplemente podemos hacer:
foo?.doSomethingInteresting()
Si
foo
contiene un valor, se llamará a este método.
Si no es así, no pasará nada malo: el código simplemente continuará ejecutándose.
(Este es un comportamiento similar al envío de mensajes a
nil
en Objective-C)
Por lo tanto, esto también se puede utilizar para establecer propiedades y métodos de llamada. Por ejemplo:
foo?.bar = Bar()
Nuevamente, nada malo sucederá aquí si
foo
es
nil
.
Su código simplemente continuará ejecutándose.
Otro buen truco que el encadenamiento opcional le permite hacer es verificar si establecer una propiedad o llamar a un método fue exitoso.
Puede hacer esto comparando el valor de retorno a
nil
.
(Esto se debe a que un valor opcional devolverá
Void?
lugar de
Void
en un método que no devuelve nada)
Por ejemplo:
if (foo?.bar = Bar()) != nil {
print("bar was set successfully")
} else {
print("bar wasn’t set successfully")
}
Sin embargo, las cosas se vuelven un poco más difíciles cuando se trata de acceder a propiedades o métodos de llamada que devuelven un valor.
Debido a que
foo
es opcional, todo lo que devuelva también será opcional.
Para lidiar con esto, puede desenvolver los opcionales que se devuelven utilizando uno de los métodos anteriores, o desenvolverlos antes de acceder a los métodos o llamar a los métodos que devuelven valores.
Además, como su nombre lo indica, puede ''encadenar'' estas declaraciones juntas.
Esto significa que si
foo
tiene una propiedad opcional
baz
, que tiene una propiedad
qux
, puede escribir lo siguiente:
let optionalQux = foo?.baz?.qux
Nuevamente, debido a que
foo
y
baz
son opcionales, el valor devuelto por
qux
siempre será opcional, independientemente de si
qux
es opcional.
map
y
flatMap
Una característica a menudo infrautilizada con opciones es la capacidad de usar las funciones
map
y
flatMap
.
Estos le permiten aplicar transformaciones no opcionales a variables opcionales.
Si un opcional tiene un valor, puede aplicarle una transformación determinada.
Si no tiene un valor, permanecerá
nil
.
Por ejemplo, supongamos que tiene una cadena opcional:
let anOptionalString:String?
Al aplicarle la función de
map
, podemos usar la función
stringByAppendingString
para concatenarla a otra cadena.
Debido a que
stringByAppendingString
toma un argumento de cadena no opcional, no podemos ingresar nuestra cadena opcional directamente.
Sin embargo, al usar
map
, podemos usar allow
stringByAppendingString
para usar si
anOptionalString
tiene un valor.
Por ejemplo:
var anOptionalString:String? = "bar"
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // Optional("foobar")
Sin embargo, si
anOptionalString
no tiene un valor,
map
devolverá
nil
.
Por ejemplo:
var anOptionalString:String?
anOptionalString = anOptionalString.map {unwrappedString in
return "foo".stringByAppendingString(unwrappedString)
}
print(anOptionalString) // nil
flatMap
funciona de manera similar al
map
, excepto que le permite devolver
otro
opcional desde el cuerpo del cierre.
Esto significa que puede ingresar un opcional en un proceso que requiere una entrada no opcional, pero puede generar un opcional en sí mismo.
try!
El sistema de manejo de errores de Swift se puede usar de manera segura con Do-Try-Catch :
do {
let result = try someThrowingFunc()
} catch {
print(error)
}
Si
someThrowingFunc()
arroja un error, el error se capturará de forma segura en el bloque
catch
.
La constante de
error
que ve en el bloque
catch
no ha sido declarada por nosotros, se genera automáticamente por
catch
.
También puede declarar el
error
usted mismo, tiene la ventaja de poder transmitirlo a un formato útil, por ejemplo:
do {
let result = try someThrowingFunc()
} catch let error as NSError {
print(error.debugDescription)
}
Usar
try
esta manera es la forma correcta de probar, capturar y manejar errores provenientes de funciones de lanzamiento.
También hay
try?
que absorbe el error:
if let result = try? someThrowingFunc() {
// cool
} else {
// handle the failure, but there''s no error information available
}
¡Pero el sistema de manejo de errores de Swift también proporciona una forma de "forzar el intento" con el
try!
:
let result = try! someThrowingFunc()
Los conceptos explicados en esta publicación también se aplican aquí: si se produce un error, la aplicación se bloqueará.
¡Solo debes usar
try!
si puede probar que su resultado nunca fallará en su contexto, y esto es muy raro.
La mayoría de las veces usará el sistema completo Do-Try-Catch, y el opcional,
try?
, en los raros casos en los que manejar el error no es importante.
Recursos
- Optional
- Cuándo usar y cuándo no usar opciones implícitas sin envolver
- Aprende a depurar el bloqueo de una aplicación iOS