Mi programa Swift se está bloqueando EXC_BAD_INSTRUCTION
y uno de los siguientes errores similares. ¿Qué significa este error y cómo lo soluciono?
Error fatal: inesperadamente encontrado nulo al desenvolver un valor opcional
o
Error fatal: se encontró inesperadamente nulo al desenvolver implícitamente 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 editar la respuesta wiki existente.
swift
exception
error-handling
pkamb
fuente
fuente
Respuestas:
Esta respuesta es wiki comunitaria . Si crees que podría mejorarse, ¡puedes editarlo !
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-1
usarse para indicar la ausencia de un número entero, o tal vezINT_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 se puede tratar como un valor válido .Swift es un lenguaje de tipo seguro, lo que significa que el lenguaje le ayuda a tener claridad sobre 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 o el valor especial
nil
.Los opcionales se definen con un
?
sufijo en el tipo:La falta de un valor en un opcional se indica mediante
nil
:(Tenga en cuenta que esto
nil
no es lo mismo quenil
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's 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 desenvolvimiento forzado:
1. Desenvolvimiento de fuerza explícito
Esto se hace con el
!
operador de forma opcional. Por ejemplo:Como
anOptionalString
está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.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á.
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 los puntos de venta antes de que se carguen. También debe verificar que las conexiones sean correctas en su archivo storyboard / xib, de lo contrario, los valores estarán
nil
en tiempo de ejecución y, por lo tanto, se bloquearán cuando se desenvuelvan implícitamente . 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 los que el uso!
sea aceptable, pero solo debería usarlo si está 100% seguro de que lo opcional contiene un valor.Si bien no puede ser una ocasión donde se puede utilizar la fuerza de desenvolver, como usted sabe que es un hecho de que un opcional contiene un valor - no hay un solo lugar donde no se puede con seguridad que desenvuelva opcional en lugar.
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, aunque la asignación de nulo es válida.
Solo debe usar opciones opcionales implícitamente sin envolver como último recurso . Si puede usar una variable perezosa o proporcionar un valor predeterminado para una variable, debe hacerlo en lugar de usar una opción implícitamente desenvuelta.
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
.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 {...}
oif var x = anOptional {...}
, dependiendo de si necesita modificar el valor de la nueva variable después de vincularla.Por ejemplo:
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 luego 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.
Otro buen truco es que también puede usar comas para verificar una determinada condición en el valor, después de desenvolverlo.
El único inconveniente con el uso del enlace opcional dentro de una instrucción if es que solo puede acceder al valor sin envolver dentro del alcance de la instrucció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 sintaxis
guard condition else {...}
.Entonces, para usarlos con un enlace opcional, puede hacer esto:
(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 desenvolverá y se asignará a la nuevanumber
constante. 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:
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
where
cláusula.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
, dondea
es un tipo opcional yb
es del mismo tipo quea
(aunque generalmente no es opcional).Básicamente te permite decir "Si
a
contiene un valor, desenvuélvelo. Si no es así, regreseb
en su lugar ". Por ejemplo, podría usarlo así:Esto definirá una
number
constante deInt
tipo, que contendrá el valor deanOptionalInt
, si contiene un valor, o de lo0
contrario.Es solo una abreviatura de:
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 con el sufijo del nombre de la variable con a
?
cuando se usa.Por ejemplo, supongamos que tenemos una variable
foo
, de tipo unaFoo
instancia opcional .Si quisiéramos llamar a un método
foo
que no devuelve nada, simplemente podemos hacer: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
nil
en Objective-C)Por lo tanto, esto también se puede utilizar para establecer propiedades y métodos de llamada. Por ejemplo:
De nuevo, nada malo va a pasar aquí si
foo
esnil
. 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 con
nil
.(Esto se debe a que un valor opcional devolverá en
Void?
lugar deVoid
en un método que no devuelve nada)Por ejemplo:
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. Como
foo
es opcional, todo lo que devuelva también será opcional. Para lidiar con esto, puede desenvolver las opciones que se devuelven utilizando uno de los métodos anteriores, o desenvolversefoo
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 opcionalbaz
, que tiene una propiedadqux
, puede escribir lo siguiente:Nuevamente, porque
foo
ybaz
son opcionales, el valor devuelto desdequx
siempre será opcional independientemente de siqux
es opcional.map
yflatMap
Una característica a menudo infrautilizada con opciones es la capacidad de usar las funciones
map
yflatMap
. 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:
Al aplicarle la
map
función, podemos usar lastringByAppendingString
función para concatenarla en otra cadena.Debido a que
stringByAppendingString
toma un argumento de cadena no opcional, no podemos ingresar nuestra cadena opcional directamente. Sin embargo, al usarmap
, podemos usar permitirstringByAppendingString
que se use sianOptionalString
tiene un valor.Por ejemplo:
Sin embargo, si
anOptionalString
no tiene un valor,map
volveránil
. Por ejemplo:flatMap
funciona de manera similar amap
, 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 :
Si
someThrowingFunc()
arroja un error, el error quedará atrapado con seguridad en elcatch
bloque.La
error
constante que ve en elcatch
bloque no ha sido declarada por nosotros, se genera automáticamente porcatch
.También puede declararse
error
, tiene la ventaja de poder convertirlo a un formato útil, por ejemplo:Usar
try
esta forma es la forma correcta de intentar, atrapar y manejar los errores que provienen de las funciones de lanzamiento.También hay
try?
que absorbe el error:Pero el sistema de manejo de errores de Swift también proporciona una manera de "forzar el intento" con
try!
:Los conceptos explicados en esta publicación también se aplican aquí: si se produce un error, la aplicación se bloqueará.
Solo debe usarlo
try!
si puede demostrar 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
fuente
compactMap()
lugar deflatMap()
.TL; respuesta DR
Con muy pocas excepciones , esta regla es dorada:
Evitar el uso de
!
Declarar variable opcional (
?
), no implícitamente opciones 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
oguard let
Cualquiera de las variables de desenvolver así:
O así:
Esta respuesta pretendía ser concisa, para una comprensión completa, lea la respuesta aceptada
Recursos
fuente
Esta pregunta surge TODO EL TIEMPO en SO. Es una de las primeras cosas con las que luchan los nuevos desarrolladores de Swift.
Antecedentes:
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 no sabe lo que está haciendo, evitar 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: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:
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:
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:
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:
Se bloqueará en referencia a la propiedad de String con mayúscula 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.
fuente
guard
cláusulas. No hay nada sobre el uso de laif var
cual es una construcción tan válida comoif let
. No hay nada acerca de laswhere
cláusulas que creo que vale la pena mencionar cuando hablamos deif let
encuadernación (en muchos casos, elimina una capa completa de anidamiento). No hay nada sobre encadenamiento opcional.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
Entonces, para asignar un nulo a nuestra variable 'i'. Podemos hacer
var i = Optional<Int>.none
o asignar un valor, pasaremos algún valorvar i = Optional<Int>.some(28)
Según swift, 'nulo' es la ausencia de valor. Y para crear una instancia inicializada con
nil
Tenemos que conformarnos con un protocolo llamadoExpressibleByNilLiteral
y excelente si lo adivinas, solo se desaconsejaOptionals
conformarseExpressibleByNilLiteral
y conformarse con otros tipos.ExpressibleByNilLiteral
tiene un único método llamadoinit(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 como lo llama el compilador cada vez que inicializa un tipo Opcional connil
literal.Incluso yo mismo tengo que envolver (sin juego de palabras) mi cabeza. Opcionales: D Happy Swfting All .
fuente
Primero, debe saber qué es un valor opcional. Puedes dar un paso para lenguaje de programación Swift para obtener más detalles.
En segundo lugar, debe saber que el valor opcional tiene dos estados. Uno es el valor completo y el otro es un valor nulo. Entonces, antes de implementar un valor opcional, debe verificar qué estado es.
Puede usar
if let ...
oguard let ... else
y así sucesivamente.Otra forma, si no desea verificar el estado de la variable antes de su implementación, también puede usarlo
var buildingName = buildingName ?? "buildingName"
.fuente
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:
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:
Controlador de destino:
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.
fuente
updateView
en el controlador de destino;)updateView
no es necesario llamar al controlador de destino en este caso, ya que estoy usando laname
variable para configurarnameLabel.text
elviewDidLoad
. Pero si estuviéramos haciendo una gran configuración, seguramente sería mejor crear otra función y llamarla desde suviewDidLoad
lugar.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:
Si
someVariable
es nulo, entonces se bloqueará. Al realizar un desenvolvimiento forzado, movió la responsabilidad de verificación nula del compilador hacia 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í:
moldes forzados (abajo):
Aquí, al forzar el lanzamiento, le dice al compilador que ya no se preocupe, ya que siempre tendrá una
Rectangle
instancia 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í:
Opciones implícitamente sin envolver. Supongamos que tiene la siguiente definición de clase:
Ahora, si nadie se equivoca con la
name
propiedad configurándola ennil
, entonces funciona como se esperaba, sin embargo, siUser
se inicializa desde un JSON que carece de laname
clave, se 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 nulo para el momento en que necesite ser utilizada. 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 desconectadas o aún no conectadas Este es un caso particular del escenario # 3. Básicamente tiene alguna clase cargada con XIB que desea usar.
Ahora, si no conectó la salida del 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 utilizar 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:
Ahora, si no se especifican anotaciones de nulabilidad (ya sea explícitamente o vía
NS_ASSUME_NONNULL_BEGIN
/NS_ASSUME_NONNULL_END
), laname
propiedad se importará en Swift comoString!
(una IUO - opcional sin envoltura implícita). Tan pronto como un código rápido quiera usar el valor, se bloqueará siname
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 cuando se trata de nulabilidad, puede terminar con valores nulos, incluso si los marcó explícitamente como
nonnull
.fuente
Este es un comentario más importante y por eso las opciones implícitamente desenvueltas pueden ser engañosas cuando se trata de depurar
nil
valores.Piense en el siguiente código: Se compila sin errores / advertencias:
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:
Larga historia corta al usar
var address : Address!
está ocultando la posibilidad de que una variable pueda sernil
de otros lectores. Y cuando se bloquea, estás como "¡¿Qué demonios ?! miaddress
no es opcional, así que ¿por qué me estrello?".Por lo tanto, es mejor escribir como tal:
¿Puedes decirme qué objeto es ese
nil
?Esta vez, el código se te ha dejado más claro. Puede racionalizar y pensar que es probable que sea el
address
parámetro que se desenvolvió con fuerza.El código completo sería:
fuente
Los errores
EXC_BAD_INSTRUCTION
yfatal error: unexpectedly found nil while implicitly unwrapping an Optional value
aparecen más cuando ha declarado 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.
fuente
@IBOutlet
causa de este error tener el error Fatal: se encontró inesperadamente nulo mientras se desenvolvía implícitamente una versión de valor opcional del error?Si obtiene este error en CollectionView intente crear un archivo CustomCell y Custom xib también.
agregue este código en ViewDidLoad () en mainVC.
fuente
Encontré este error al pasar de un controlador de vista de tabla a un controlador de vista porque había olvidado especificar el nombre de clase personalizado para el controlador de vista en el guión gráfico principal.
Algo simple que vale la pena verificar si todo lo demás se ve bien
fuente