¿Qué significa "Error fatal: inesperadamente encontrado nulo al desenvolver un valor opcional"?

Aug 24 2015

Mi programa Swift se bloquea con EXC_BAD_INSTRUCTIONuno 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: inesperadamente encontrado nulo al desenvolver implícitamente un valor opcional


Esta publicación está destinada a recopilar respuestas a problemas "nulos encontrados inesperadamente", 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.

Respuestas

685 Hamish Aug 24 2015 at 02:10

Esta respuesta es la wiki de la comunidad . Si cree que se puede mejorar, ¡no dude en editarlo !

Antecedentes: ¿Qué es un opcional?

En Swift, Optional<Wrapped>es un tipo de opción : puede contener cualquier valor del tipo original ("Envuelto") o ningún valor (el valor especial nil). Un valor opcional que debe ser desempaquetado antes de que pueda ser utilizado.

Opcional es un tipo genérico , lo que significa que Optional<Int>y Optional<String>son tipos distintos; el tipo interior <>se llama tipo envuelto. Debajo del capó, un Optional es una enumeración con dos casos: .some(Wrapped)y .none, donde .nonees equivalente a nil.

Los opcionales se pueden declarar usando el tipo nombrado Optional<T>o (más comúnmente) como una abreviatura con un ?sufijo.

var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int?  // `nil` is the default when no value is provided
var aVerboseOptionalInt: Optional<Int>  // equivalent to `Int?`

anOptionalInt = nil // now this variable contains nil instead of an integer

Los opcionales son una herramienta simple pero poderosa para expresar sus suposiciones mientras escribe código. El compilador puede utilizar esta información para evitar que cometa errores. Desde el lenguaje de programación Swift :

Swift es un lenguaje de tipo seguro , lo que significa que el lenguaje le ayuda a tener claro los tipos de valores con los que puede trabajar su código. Si parte de su código requiere un String, el tipo de seguridad le impide pasarlo Intpor error. Del mismo modo, la seguridad de tipos evita que pase accidentalmente un opcional Stringa un fragmento de código que requiere un no opcional String. La seguridad de tipos le ayuda a detectar y corregir errores lo antes posible en el proceso de desarrollo.

Algunos otros lenguajes de programación también tienen tipos de opciones genéricas : por ejemplo, Quizás en Haskell, opción en Rust y opcional en C ++ 17.

En lenguajes de programación sin tipos de opciones, a menudo se utiliza un valor "centinela" particular para indicar la ausencia de un valor válido. En Objective-C, por ejemplo, nil(el puntero nulo ) representa la falta de un objeto. Para tipos primitivos como int, no se puede usar un puntero nulo, por lo que necesitaría una variable separada (como value: Inty isValid: Bool) o un valor centinela designado (como -1o INT_MIN). Estos enfoques son propensos a errores porque es fácil olvidarse de verificar isValido verificar el valor centinela. Además, si se elige un valor en particular como centinela, eso significa que ya no se puede tratar como un valor válido .

Los tipos de opciones como Swift Optionalresuelven estos problemas introduciendo un nilvalor especial separado (para que no tenga que designar un valor centinela) y aprovechando el sistema de tipos fuertes para que el compilador pueda ayudarle a recordar comprobar si es necesario.


¿Por qué recibí un " error fatal: inesperadamente encontrado nulo al desenvolver un valor opcional "?

Para acceder al valor de un opcional (si tiene alguno), debe desenvolverlo . Un valor opcional se puede desenvolver de forma segura o forzada. Si fuerza-desenvuelve un opcional y no tiene un valor, su programa fallará 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. Desenvolver forzado explícito

Esto se hace con el !operador de forma opcional. Por ejemplo:

let anOptionalString: String?
print(anOptionalString!) // <- CRASH

Error fatal: inesperadamente encontrado nulo al desenvolver un valor opcional

Como anOptionalStringestá nilaquí, se producirá un bloqueo en la línea que obligará a desenvolverlo.

2. Opcionales implícitamente no envueltos

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 un opcional implícitamente desenvuelto, se forzará automáticamente a desenvolverlo. Si no contiene un valor, se bloqueará.

print(optionalDouble) // <- CRASH

Error fatal: inesperadamente encontrado nulo al desenvolver implícitamente un valor opcional

Para averiguar 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, suelen ser opcionales sin envolver implícitamente. Esto se debe a que su xib o guión gráfico conectará 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 guión gráfico / archivo xib; de lo contrario, los valores estarán nilen tiempo de ejecución y, por lo tanto, fallarán cuando se desenvuelvan implícitamente . Cuando arregle 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 la apertura de un opcional?

Desenvolvimiento explícito de la fuerza

Como regla general, nunca debe forzar explícitamente el desencadenamiento 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 el 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.

Opcionales implícitamente no envueltos

Estas variables están diseñadas para que pueda posponer 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 un desenvolvimiento forzado, siguen siendo intrínsecamente inseguros, ya que suponen que su valor no es nulo, aunque asignar nulo es válido.

Solo debe utilizar opcionales sin envolver implícitamente como último recurso . Si puede usar una variable diferida o proporcionar un valor predeterminado para una variable, debe hacerlo en lugar de usar un opcional implícitamente sin empaquetar.

Sin embargo, hay algunos escenarios en los que los opcionales no envueltos implícitamente son beneficiosos y aún puede utilizar varias formas de desenvolverlos de forma segura como se indica a continuación, pero siempre debe utilizarlos con la debida precaución.


¿Cómo puedo lidiar con los opcionales de forma segura?

La forma más sencilla de comprobar 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% de las veces cuando se trabaja con opcionales, en realidad querrá acceder al valor que contiene, si es que contiene uno. Para hacer esto, puede utilizar el enlace opcional .

Encuadernación opcional

El enlace opcional le permite verificar si un opcional contiene un valor, y le permite asignar el valor no envuelto 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 hace es comprobar primero que el opcional contiene un valor. Si lo hace , entonces el valor de 'envolver' se asigna a una nueva variable ( number) - que luego se puede utilizar libremente como si se tratara de no opcional. Si el opcional no contiene un valor, entonces se invocará la cláusula else, como es de esperar.

Lo bueno de la encuadernación opcional es que puede desenvolver varios opcionales al mismo tiempo. Puede separar las declaraciones con una coma. La declaración tendrá éxito si se desenvuelven todos los opcionales.

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 de un enlace opcional dentro de una declaración if es que solo puede acceder al valor no envuelto desde el alcance de la declaración. Si necesita acceder al valor desde fuera del alcance de la declaración, puede utilizar una declaración de protección .

Una declaración de guardia 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:

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 anOptionalIntcontiene un valor, se desenvolverá y se asignará a la nueva numberconstante. El código después del guardia continuará ejecutándose. Si no contiene un valor, el guardia ejecutará el código entre corchetes, lo que conducirá a la transferencia de control, de modo que el código inmediatamente posterior no se ejecutará.

Lo realmente interesante de las declaraciones de protección es que el valor sin empaquetar ahora está disponible para usar en el código que sigue a la declaración (ya que sabemos que el código futuro solo se puede ejecutar si el opcional tiene un valor). Esto es excelente para eliminar 'pirámides de fatalidad' creadas al anidar múltiples declaraciones 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 ingeniosos que admite la declaración if, como desenvolver múltiples opcionales al mismo tiempo y usar la wherecláusula.

El uso de una declaración if o guard depende completamente de si algún código futuro requiere que el opcional contenga un valor.

Operador de coalescencia nula

El operador coalescente nulo es una ingeniosa versión abreviada del operador condicional ternario , diseñado principalmente para convertir opcionales en no opcionales. Tiene la sintaxis a ?? b, donde aes un tipo opcional y bes del mismo tipo que a(aunque normalmente no es opcional).

Básicamente le permite decir “Si acontiene un valor, desenvuélvalo. Si no es así, regresa ben su lugar ”. Por ejemplo, podrías usarlo así:

let number = anOptionalInt ?? 0

Esto definirá una numberconstante de Inttipo, que contendrá el valor de anOptionalInt, si contiene un valor, o de 0otro modo.

Es solo una abreviatura de:

let number = anOptionalInt != nil ? anOptionalInt! : 0

Encadenamiento opcional

Puede utilizar el encadenamiento opcional para llamar a un método o acceder a una propiedad en un opcional. Esto se hace simplemente añadiendo un sufijo en el nombre de la variable ?cuando se usa.

Por ejemplo, digamos que tenemos una variable foo, de tipo una Fooinstancia opcional .

var foo : Foo?

Si quisiéramos llamar a un método fooque no devuelve nada, simplemente podemos hacer:

foo?.doSomethingInteresting()

Si foocontiene 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 nilen 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, no pasará nada malo aquí si fooes así nil. Su código simplemente continuará ejecutándose.

Otro buen truco que le permite hacer el encadenamiento opcional es verificar si la configuración de una propiedad o la llamada a un método fue exitosa. Puede hacer esto comparando el valor de retorno con nil.

(Esto se debe a que se devolverá un valor opcional en Void?lugar de Voidun 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 complicadas al intentar acceder a propiedades o llamar a métodos que devuelven un valor. Como fooes opcional, todo lo que se devuelva también será opcional. Para lidiar con esto, puede desenvolver los opcionales que se devuelven utilizando uno de los métodos anteriores, o desenvolverse fooantes de acceder a los métodos o llamar a los métodos que devuelven valores.

Además, como sugiere el nombre, puede "encadenar" estas declaraciones. Esto significa que si footiene una propiedad opcional baz, que tiene una propiedad qux, puede escribir lo siguiente:

let optionalQux = foo?.baz?.qux

Nuevamente, debido a que fooy bazson opcionales, el valor devuelto quxsiempre será opcional, independientemente de si él quxmismo es opcional.

map y flatMap

Una característica a menudo infrautilizada con opcionales es la capacidad de utilizar las funciones mapy 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 valor, permanecerá nil.

Por ejemplo, digamos que tiene una cadena opcional:

let anOptionalString:String?

Al aplicarle la mapfunción, podemos usar la stringByAppendingStringfunción para concatenarla a otra cadena.

Debido a que stringByAppendingStringtoma un argumento de cadena no opcional, no podemos ingresar nuestra cadena opcional directamente. Sin embargo, al usar map, podemos usar permitir stringByAppendingStringque se use si anOptionalStringtiene un valor.

Por ejemplo:

var anOptionalString:String? = "bar"

anOptionalString = anOptionalString.map {unwrappedString in
    return "foo".stringByAppendingString(unwrappedString)
}

print(anOptionalString) // Optional("foobar")

Sin embargo, si anOptionalStringno tiene un valor, mapregresará nil. Por ejemplo:

var anOptionalString:String?

anOptionalString = anOptionalString.map {unwrappedString in
    return "foo".stringByAppendingString(unwrappedString)
}

print(anOptionalString) // nil

flatMapfunciona de manera similar a map, excepto que le permite devolver otro opcional desde dentro del 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í.

try!

El sistema de manejo de errores de Swift se puede utilizar de forma segura con Do-Try-Catch :

do {
    let result = try someThrowingFunc() 
} catch {
    print(error)
}

Si someThrowingFunc()arroja un error, el error quedará atrapado de forma segura en el catchbloque.

La errorconstante que ves en el catchbloque no ha sido declarada por nosotros, la genera automáticamente catch.

También puedes declararte errortú mismo, tiene la ventaja de poder convertirlo en un formato útil, por ejemplo:

do {
    let result = try someThrowingFunc()    
} catch let error as NSError {
    print(error.debugDescription)
}

Usar tryesta forma es la forma correcta de probar, detectar y manejar errores provenientes de funciones de lanzamiento.

También hay lo 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 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 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 utilizará el sistema Do-Try-Catch completo, y el opcional try?, en los raros casos en los que manejar el error no es importante.


Recursos

65 Sajjon Dec 19 2016 at 20:26

TL; respuesta DR

Con muy pocas excepciones , esta regla es de oro:

Evite el uso de !

Declarar variable opcional ( ?), no opcionales implícitamente desenvueltos (IUO) ( !)

En otras palabras, mejor usa:
var nameOfDaughter: String?

En vez de:
var nameOfDaughter: String!

Desenvolver variable opcional usando if letoguard let

Desenvuelva la variable como esta:

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 tenía la intención de ser concisa, para una comprensión completa leer respuesta aceptada


Recursos

40 DuncanC Feb 28 2016 at 21:37

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 podrían 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í.

Los opcionales Swift le permiten configurar una variable de cualquier tipo para que contenga 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 sin valor).

Un opcional es en realidad un contenedor que contiene una variable de un tipo determinado o nada.

Es necesario "desenvolver" un opcional para poder recuperar el valor que contiene.

Los "!" El operador es un operador de "desenvolver forzado". Dice "confía en mí. Sé lo que estoy haciendo. Te garantizo que cuando se ejecute este código, la variable no contendrá nil". Si te equivocas, te estrellas.

A menos que realmente no sabe lo que está haciendo, evitar el "!" forzar al operador desenvolver. Probablemente sea la mayor fuente de fallas para los programadores principiantes de Swift.

Cómo lidiar con opcionales:

Hay muchas otras formas de lidiar con los opcionales que son más seguras. Aquí hay algunos (no es 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 declaración if ".

Aquí hay un ejemplo de enlace opcional con nuestro fooopcional:

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 usa las ofertas opcionales solo existe (solo está "dentro del alcance") en el cuerpo de la instrucción if.

Alternativamente, puede usar una declaración de guardia, 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 de un enlace opcional "if let".

También hay una construcción llamada "operador coalescente nulo". Toma la forma "option_var ?? replacement_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 error un poco más sutil con los opcionales es "los opcionales implícitamente desenvueltos. Cuando declaramos foo, podríamos decir:

var foo: String!

En ese caso, foo sigue siendo opcional, pero no tiene que desenvolverlo para hacer referencia a él. Eso significa que cada vez que intente hacer referencia a foo, se bloqueará si es nulo.

Entonces este código:

var foo: String!


let upperFoo = foo.capitalizedString

Se bloqueará en referencia a la propiedad capitalizedString de foo aunque no estemos desenvolviendo a la fuerza foo. la impresión se ve bien, pero no lo es.

Por lo tanto, debe tener mucho cuidado con los opcionales implícitamente desenvueltos. (y tal vez incluso evitarlos por completo hasta que tenga un conocimiento sólido de los opcionales).

En pocas palabras: cuando esté aprendiendo Swift por primera vez, finja el "!" el carácter no es parte del idioma. Es probable que te meta en problemas.

13 Tharzeez Nov 17 2017 at 23:15

Dado que las respuestas anteriores explican claramente cómo jugar de forma segura con Optionals. Intentaré explicar qué son realmente los opcionales en Swift.

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 cero a nuestra variable 'i'. Podemos hacer var i = Optional<Int>.none o asignar un valor, pasaremos algún valor var i = Optional<Int>.some(28)

Según Swift, 'nil' es la ausencia de valor. Y para crear una instancia inicializado con nilTenemos que cumplir con un protocolo llamado ExpressibleByNilLiteraly grande si lo has adivinado, sólo se Optionalsajusta a ExpressibleByNilLiteraly que se ajuste a otros tipos no se recomienda.

ExpressibleByNilLiteraltiene un solo 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 nilliteral.

Incluso yo mismo tengo que envolver (sin juego de palabras) mi cabeza con Opcionales: D Happy Swfting All .

12 QiunCheng Jul 30 2016 at 14:18

Primero, debe saber qué es un valor opcional. Puede pasar al 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 total y el otro es un valor nulo. Entonces, antes de implementar un valor opcional, debe verificar en qué estado se encuentra.

Puede usar if let ...o, guard let ... elseetc.

De otra forma, si no desea verificar el estado de la variable antes de su implementación, también puede usar var buildingName = buildingName ?? "buildingName"en su lugar.

8 Wissa Apr 13 2018 at 21:06

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 configurar 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 que tenga 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 en sí directamente.

7 Cristik Sep 19 2018 at 13:04

Básicamente, trataste de usar un valor nulo en lugares donde Swift solo permite valores que no sean nulos, diciéndole al compilador que confíe en ti que nunca habrá valor nulo allí, permitiendo así que tu aplicación se compile.

Hay varios escenarios que conducen a este tipo de error fatal:

  1. desenvuelve forzado:

    let user = someVariable!
    

    Si someVariablees nulo, se producirá un bloqueo. Al hacer un desenvolvimiento forzado, trasladó la responsabilidad de verificación nula del compilador a usted, básicamente al hacer un desenvolvimiento forzado, le garantiza al compilador que nunca tendrá valores nulos allí. ¿Y adivinen qué sucede si de alguna manera termina en un valor nulo someVariable?

    ¿Solución? Use un enlace opcional (también conocido como if-let), realice el procesamiento de la variable allí:

    if user = someVariable {
        // do your stuff
    }
    
  2. lanzamientos forzados (abajo):

    let myRectangle = someShape as! Rectangle
    

    Aquí, al forzar la conversión, le dice al compilador que no se preocupe más, ya que siempre tendrá una Rectangleinstancia allí. Y mientras eso se mantenga, no tiene que preocuparse. Los problemas comienzan cuando usted o sus colegas del proyecto comienzan a circular valores que no son rectangulares.

    ¿Solución? Use un enlace opcional (también conocido como if-let), realice el procesamiento de la variable allí:

    if let myRectangle = someShape as? Rectangle {
        // yay, I have a rectangle
    }
    
  3. Opcionales implícitamente desenvueltos. 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 namepropiedad al establecerla en nil, entonces funciona como se esperaba, sin embargo, si Userse inicializa desde un JSON que carece de la nameclave, 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 no nulo para cuando 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 al decirle las rutas de código que se perdió dando un valor a esa propiedad

  4. Tomacorrientes desconectados o aún no conectados. 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 desee utilizar 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 le obligará a desenvolverla en todo el código.

  5. Valores que provienen 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), entonces la namepropiedad se importará en Swift como String!(un IUO - implícitamente no envuelto opcional). Tan pronto como algún código rápido quiera usar el valor, se bloqueará si namees nulo.

    ¿Solución? Agregue anotaciones de nulabilidad a su código Objective-C. Sin embargo, tenga cuidado, el compilador de Objective-C es un poco permisivo cuando se trata de nulabilidad, podría terminar con valores nulos, incluso si los marcó explícitamente como nonnull.

2 Honey Nov 01 2018 at 11:39

Este es un comentario más importante y explica por qué los opcionales implícitamente desenvueltos pueden ser engañosos cuando se trata de depurar nilvalores.

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: inesperadamente encontrado nulo al desenvolver un valor opcional

¿Puedes decirme 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
}

En pocas palabras, al usar var address : Address!estás ocultando la posibilidad de que una variable pueda ser nilde otros lectores. Y cuando se bloquea, piensas "¡¿qué diablos ?! mi addressno es opcional, entonces, ¿por qué me estoy estrellando?".

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 ahora qué objeto era nil?

Esta vez el código te ha quedado más claro. Puede racionalizar y pensar que probablemente sea el addressparámetro 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
}
2 AleMohamad May 21 2019 at 00:34

Los errores EXC_BAD_INSTRUCTIONy fatal error: unexpectedly found nil while implicitly unwrapping an Optional valueaparecen 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 , mencionado en otras respuestas, pero esta es la única vez que me parece mayormente.

2 ShigaSuresh Feb 13 2020 at 01:16

Si recibe este error en CollectionView, intente crear un archivo CustomCell y Custom xib también.

agregue este código en ViewDidLoad () en mainVC.

    let nib = UINib(nibName: "CustomnibName", bundle: nil)
    self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")
MikeVolmar Sep 26 2019 at 22:11

Me encontré con este error al hacer una transición 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