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

415

Mi programa Swift se está bloqueando EXC_BAD_INSTRUCTIONy 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.

pkamb
fuente
8
@RobertColumbia, este es un par de preguntas y respuestas de Wiki de la comunidad con una amplia documentación del problema, no creo que esta pregunta deba cerrarse por falta de MCVE.
JAL
Crear una variable como esta [var nameOfDaughter: String? ] y usar esa variable como esta [if let _ = nameOfDaughter {}] es el mejor enfoque.
iOS

Respuestas:

674

Esta respuesta es wiki comunitaria . Si crees que podría mejorarse, ¡puedes editarlo !

Antecedentes: ¿Qué es un opcional?

En Swift, Optionales 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 -1usarse 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 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:

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 mediante nil:

anOptionalInt = nil

(Tenga en cuenta que esto nilno es lo mismo que nilen Objective-C. En Objective-C, niles 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.

línea estrellada

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:

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

Error fatal: inesperadamente encontrado nulo al desenvolver un valor opcional

Como anOptionalStringestá nilaquí, 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

Error grave : se encontró inesperadamente nulo al desenvolver implícitamente un valor opcional

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

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

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 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:

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 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 whereclá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, donde aes un tipo opcional y bes del mismo tipo que a(aunque generalmente no es opcional).

Básicamente te permite decir "Si acontiene un valor, desenvuélvelo. Si no es así, regrese ben su lugar ". Por ejemplo, podría usarlo así:

let number = anOptionalInt ?? 0

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

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 con el sufijo del nombre de la variable con a ?cuando se usa.

Por ejemplo, supongamos 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()

De nuevo, nada malo va a pasar aquí si fooes 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 con nil.

(Esto se debe a que un valor opcional devolverá en Void?lugar de Voiden 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. Como fooes 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 desenvolverse fooantes 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 footiene una propiedad opcional baz, que tiene una propiedad qux, puede escribir lo siguiente:

let optionalQux = foo?.baz?.qux

Nuevamente, porque fooy bazson opcionales, el valor devuelto desde quxsiempre será opcional independientemente de si quxes opcional.

map y flatMap

Una característica a menudo infrautilizada con opciones es la capacidad de usar 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 un valor, permanecerá nil.

Por ejemplo, supongamos que tiene una cadena opcional:

let anOptionalString:String?

Al aplicarle la mapfunción, podemos usar la stringByAppendingStringfunción para concatenarla en 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, mapvolverá 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 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 quedará atrapado con seguridad en el catchbloque.

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

También puede declararse error, tiene la ventaja de poder convertirlo a 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 intentar, atrapar y manejar los errores que provienen de las 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 manera 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 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

Hamish
fuente
87
Personalmente, me gustaría agradecerle por hacer el esfuerzo de escribir todo esto. Siento que esto ciertamente será útil tanto para principiantes como para expertos que usen Swift.
Pranav Wadhwa
15
Me alegra que lo hayas encontrado útil. ¡Esta respuesta es wiki comunitaria, por lo que es una colaboración entre muchas personas (7, hasta ahora)!
jtbandes
1
Este error es intermitente en mi caso. ¿Alguna sugerencia? Código en stackoverflow.com/questions/50933681/…
py_ios_dev
1
Tal vez es hora de actualizar esta respuesta para usarla en compactMap()lugar de flatMap().
Nicolas Miari
así que supongo que la mejor solución es el "Operador de fusión nula", ¿verdad?
mehdigriche
65

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 letoguard 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


Recursos

Sajjon
fuente
40

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

Duncan C
fuente
77
Deberías considerar hacer de esto un wiki público.
vacawama
3
Edite su respuesta, y debajo del cuadro de edición a la derecha hay una casilla de verificación wiki de la comunidad . Ya no obtendrá representante para la respuesta, pero sé que de todos modos esto no es una respuesta evidente.
vacawama
66
Dado que esta pregunta se hace un millón de veces en el sitio, si me tomara el tiempo de publicar una pregunta con respuesta propia como respuesta canónica a la pregunta, espero que la respuesta sea mucho más completa que esta. No hay nada sobre las guardcláusulas. No hay nada sobre el uso de la if varcual es una construcción tan válida como if let. No hay nada acerca de las wherecláusulas que creo que vale la pena mencionar cuando hablamos de if letencuadernación (en muchos casos, elimina una capa completa de anidamiento). No hay nada sobre encadenamiento opcional.
nhgrif
66
Y cuando menciona opciones opcionales implícitamente sin envolver, ni siquiera menciona que puede usar todos los trucos opcionales antes mencionados para desenvolver de manera segura opciones opcionales sin envoltura implícita. Tampoco cubrimos desenvolver múltiples opciones en la misma cláusula.
nhgrif
1
@nhgrif, dije "También podría usar el manejo de errores try / catch o guard, pero en general una de las otras técnicas anteriores es más limpia". Ciertamente tienes algunos buenos puntos. Este es un tema bastante amplio. ¿Qué tal contribuir con su propia respuesta que cubre cosas que no hice en lugar de hacer comentarios francotiradores? De esa manera, la pregunta y sus respuestas se convierten en un mejor activo para el sitio.
Duncan C
13

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 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 nilTenemos que conformarnos con un protocolo llamado ExpressibleByNilLiteraly excelente si lo adivinas, solo se desaconseja Optionalsconformarse ExpressibleByNilLiteraly conformarse con otros tipos.

ExpressibleByNilLiteraltiene 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 como lo llama el compilador cada vez que inicializa un tipo Opcional con nilliteral.

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

Tharzeez
fuente
12

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 ...o guard let ... elsey 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".

QiunCheng
fuente
8

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.

Wissa
fuente
Simplemente no olvide mencionar dónde llamar updateViewen el controlador de destino;)
Ahmad F
@AhmadF buen punto. Sin embargo, updateViewno es necesario llamar al controlador de destino en este caso, ya que estoy usando la namevariable para configurar nameLabel.textel viewDidLoad. Pero si estuviéramos haciendo una gran configuración, seguramente sería mejor crear otra función y llamarla desde su viewDidLoadlugar.
Wissa
7

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:

  1. desenvolturas forzadas:

    let user = someVariable!

    Si someVariablees 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 en someVariable?

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

    if user = someVariable {
        // do your stuff
    }
  2. 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 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 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
    }
  3. 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 namepropiedad configurándola 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 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

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

    class SignInViewController: UIViewController {
    
        @IBOutlet var emailTextField: UITextField!
    }

    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.

  5. 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 vía NS_ASSUME_NONNULL_BEGIN/ NS_ASSUME_NONNULL_END), la namepropiedad se importará en Swift como String!(una IUO - opcional sin envoltura implícita). Tan pronto como un 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 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.

revs Cristik
fuente
2

Este es un comentario más importante y por eso las opciones implícitamente desenvueltas pueden ser engañosas 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: 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 al usar var address : Address!está ocultando la posibilidad de que una variable pueda ser nilde otros lectores. Y cuando se bloquea, estás como "¡¿Qué demonios ?! mi addressno 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 ese nil?

Esta vez, el código se te ha dejado más claro. Puede racionalizar y pensar que es probable que 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
}
miel
fuente
2

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 , mencionados en otras respuestas, pero esta es la única vez que me parece más importante.

Ale Mohamad
fuente
¿No debería la @IBOutletcausa 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?
pkamb
1
Es verdad. Quizás cuando envié la respuesta a eso me refería, y copié y pegué el primer mensaje de error fatal. La respuesta de Hamish parece muy completa sobre esto.
Ale Mohamad
2

Si obtiene 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")
ShigaSuresh
fuente
0

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

Mike Volmar
fuente