O que significa “Erro fatal: inesperadamente encontrado nulo ao desembrulhar um valor opcional”?

415

O programa My Swift está causando EXC_BAD_INSTRUCTIONum erro fatal e um dos seguintes erros semelhantes. O que esse erro significa e como corrigi-lo?

Erro fatal: inesperadamente encontrado nulo ao desembrulhar um valor opcional

ou

Erro fatal: inesperadamente encontrado nulo ao desembrulhar implicitamente um valor opcional


Esta publicação pretende coletar respostas para problemas "inesperadamente encontrados", para que eles não sejam dispersos e difíceis de encontrar. Sinta-se à vontade para adicionar sua própria resposta ou editar a resposta existente da wiki.

pkamb
fonte
8
@RobertColumbia, este é um par de perguntas e respostas do Wiki da Comunidade, com extensa documentação sobre o problema. Acho que essa pergunta não deve ser encerrada por falta de um MCVE.
JAL
Crie uma variável como esta [var nameOfDaughter: String? ] e use essa variável como esta [se deixe _ = nameOfDaughter {}] é a melhor abordagem.
iOS

Respostas:

673

Esta resposta é wiki da comunidade . Se você acha que poderia melhorar, fique à vontade para editá-lo !

Histórico: O que é opcional?

No Swift, Optionalé um tipo genérico que pode conter um valor (de qualquer tipo) ou nenhum valor.

Em muitas outras linguagens de programação, um valor "sentinela" específico é frequentemente usado para indicar uma falta de valor . No Objective-C, por exemplo, nil(o ponteiro nulo ) indica a falta de um objeto. Mas isso fica mais complicado ao trabalhar com tipos primitivos - deve -1ser usado para indicar a ausência de um número inteiro, ou talvez INT_MIN, ou de algum outro número inteiro? Se qualquer valor específico for escolhido para significar "sem número inteiro", isso significa que ele não poderá mais ser tratado como um valor válido .

Swift é uma linguagem de segurança de tipo, o que significa que a linguagem ajuda você a ter clareza sobre os tipos de valores com os quais seu código pode trabalhar. Se parte do seu código espera uma String, o tipo safety impede que você transmita um Int por engano.

No Swift, qualquer tipo pode ser opcional . Um valor opcional pode assumir qualquer valor do tipo original ou do valor especial nil.

Os opcionais são definidos com um ?sufixo no tipo:

var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int?    // `nil` is the default when no value is provided

A falta de um valor em um opcional é indicada por nil:

anOptionalInt = nil

(Observe que isso nilnão é o mesmo que nilno Objetivo-C. No Objetivo-C, nilé a ausência de um ponteiro de objeto válido ; no Swift, os Opcionais não estão restritos a objetos / tipos de referência. O opcional se comporta de maneira semelhante ao Talvez de Haskell .)


Por que recebi " erro fatal: inesperadamente encontrado nulo ao desembrulhar um valor opcional "?

Para acessar o valor de um opcional (se houver), você precisa desembrulhá- lo. Um valor opcional pode ser desembrulhado com segurança ou força. Se você forçar o desempacotamento de um opcional e ele não tiver um valor, seu programa falhará com a mensagem acima.

O Xcode mostrará a falha destacando uma linha de código. O problema ocorre nesta linha.

linha quebrada

Essa falha pode ocorrer com dois tipos diferentes de desembrulhamento forçado:

1. Desembrulhamento forçado explícito

Isso é feito com o !operador de forma opcional. Por exemplo:

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

Erro fatal: inesperadamente encontrado nulo ao desembrulhar um valor opcional

Como anOptionalStringestá nilaqui, você terá um acidente na linha em que força a desembrulhar.

2. Opcionais implicitamente desembrulhados

Eles são definidos com a !, e não ?após o tipo.

var optionalDouble: Double!   // this value is implicitly unwrapped wherever it's used

Supõe-se que esses opcionais contenham um valor. Portanto, sempre que você acessar um opcional implicitamente desembrulhado, ele será automaticamente forçado a ser desembrulhado para você. Se não contiver um valor, ele falhará.

print(optionalDouble) // <- CRASH

Erro fatal: inesperadamente encontrado nulo ao desembrulhar implicitamente um valor opcional

Para descobrir qual variável causou a falha, mantenha pressionado enquanto clica para mostrar a definição, onde você pode encontrar o tipo opcional.

Os IBOutlets, em particular, geralmente são opcionais implicitamente desembrulhados. Isso ocorre porque o seu xib ou storyboard vinculará as saídas no tempo de execução, após a inicialização. Portanto, você deve garantir que não está acessando tomadas antes que elas sejam carregadas. Você também deve verificar se as conexões estão corretas no seu arquivo storyboard / xib, caso contrário, os valores estarão nilem tempo de execução e, portanto, travarão quando forem desembrulhados implicitamente . Ao corrigir conexões, tente excluir as linhas de código que definem suas tomadas e reconecte-as.


Quando devo forçar a desembrulhar um opcional?

Desembrulhamento forçado explícito

Como regra geral, você nunca deve forçar explicitamente a desembrulhar um opcional com o !operador. Pode haver casos em que o uso !é aceitável - mas você só deve usá-lo se tiver 100% de certeza de que o opcional contém um valor.

Enquanto não pode ser uma ocasião em que você pode usar a força desembrulhar, como você sabe para um fato que um opcional contém um valor - não há um único lugar onde você não pode com segurança unwrap que opcional vez.

Opcionais implicitamente desembrulhados

Essas variáveis ​​foram projetadas para que você possa adiar sua atribuição até mais tarde no seu código. É sua responsabilidade garantir que eles tenham um valor antes de você acessá-los. No entanto, como envolvem o desembrulhamento forçado, eles ainda são inerentemente inseguros - pois assumem que seu valor é nulo, mesmo que a atribuição nula seja válida.

Você deve usar apenas opcionais implicitamente desembrulhados como último recurso . Se você pode usar uma variável lenta ou fornecer um valor padrão para uma variável - faça-o em vez de usar um opcional implicitamente desembrulhado.

No entanto, existem alguns cenários em que os opcionais implicitamente desembrulhados são benéficos , e você ainda pode usar várias maneiras de desembrulhá-los com segurança, conforme listado abaixo - mas você deve sempre usá-los com a devida cautela.


Como posso lidar com segurança com os opcionais?

A maneira mais simples de verificar se um opcional contém um valor é compará-lo com nil.

if anOptionalInt != nil {
    print("Contains a value!")
} else {
    print("Doesn’t contain a value.")
}

No entanto, 99,9% do tempo ao trabalhar com opcionais, na verdade, você deseja acessar o valor que ele contém, se houver algum. Para fazer isso, você pode usar a opção de ligação .

Encadernação opcional

Ligação opcional permite verificar se um opcional contém um valor - e permite atribuir o valor desembrulhado a uma nova variável ou constante. Ele usa a sintaxe if let x = anOptional {...}ou if var x = anOptional {...}, dependendo se você precisar modificar o valor da nova variável após vinculá-la.

Por exemplo:

if let number = anOptionalInt {
    print("Contains a value! It is \(number)!")
} else {
    print("Doesn’t contain a number")
}

O que isso faz é primeiro verificar se o opcional contém um valor. Se isso acontecer , o valor 'desembrulhado' é atribuído a uma nova variável ( number) - que você pode usar livremente como se não fosse opcional. Se o opcional não contiver um valor, a cláusula else será invocada, como seria de esperar.

O interessante da ligação opcional é que você pode desembrulhar vários opcionais ao mesmo tempo. Você pode apenas separar as instruções com uma vírgula. A declaração terá êxito se todos os opcionais forem desembrulhados.

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")
}

Outro truque interessante é que você também pode usar vírgulas para verificar uma determinada condição no valor, depois de desembrulhá-lo.

if let number = anOptionalInt, number > 0 {
    print("anOptionalInt contains a value: \(number), and it’s greater than zero!")
}

O único problema com o uso de ligação opcional em uma instrução if é que você só pode acessar o valor desembrulhado no escopo da instrução. Se você precisar acessar o valor de fora do escopo da declaração, poderá usar uma declaração de guarda .

Uma declaração de guarda permite definir uma condição para o sucesso - e o escopo atual só continuará sendo executado se essa condição for atendida. Eles são definidos com a sintaxe guard condition else {...}.

Portanto, para usá-los com uma ligação opcional, você pode fazer o seguinte:

guard let number = anOptionalInt else {
    return
}

(Observe que dentro do corpo de guarda, você deve usar uma das instruções de transferência de controle para sair do escopo do código atualmente em execução).

Se anOptionalIntcontiver um valor, ele será desempacotado e atribuído à nova numberconstante. O código após o guarda continuará executando. Se não contiver um valor - o guarda executará o código entre parênteses, o que levará à transferência do controle, para que o código imediatamente a seguir não seja executado.

O mais interessante sobre as instruções de guarda é que o valor desembrulhado está agora disponível para uso no código que segue a instrução (como sabemos que o código futuro poderá ser executado se o opcional tiver um valor). Isso é ótimo para eliminar 'pirâmides da desgraça' criadas ao aninhar várias instruções if.

Por exemplo:

guard let number = anOptionalInt else {
    return
}

print("anOptionalInt contains a value, and it’s: \(number)!")

Os guardas também suportam os mesmos truques que a instrução if suportava, como desembrulhar vários opcionais ao mesmo tempo e usar a wherecláusula.

Se você usa uma instrução if ou guard depende completamente se algum código futuro exige que o opcional contenha um valor.

Operador de coalescência zero

O Operador de coalizão nulo é uma versão simplificada do operador condicional ternário , projetado principalmente para converter opcionais em não opcionais. Possui a sintaxe a ?? b, onde aé um tipo opcional e bé do mesmo tipo a(embora geralmente não seja opcional).

Essencialmente, permite que você diga “Se acontém um valor, desembrulhe-o. Se não, então retorne b”. Por exemplo, você pode usá-lo assim:

let number = anOptionalInt ?? 0

Isso definirá uma numberconstante do Inttipo, que conterá o valor de anOptionalInt, se ele contiver um valor ou 0outro.

É apenas uma abreviação para:

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

Encadeamento opcional

Você pode usar o Encadeamento opcional para chamar um método ou acessar uma propriedade em um opcional. Isso é feito simplesmente com o sufixo do nome da variável com a ?ao usá-lo.

Por exemplo, digamos que temos uma variável foo, do tipo uma Fooinstância opcional .

var foo : Foo?

Se quisermos chamar um método fooque não retorne nada, podemos simplesmente fazer:

foo?.doSomethingInteresting()

Se foocontiver um valor, este método será chamado nele. Caso contrário, nada de ruim acontecerá - o código simplesmente continuará executando.

(Esse é um comportamento semelhante ao envio de mensagens nilno Objective-C)

Portanto, isso também pode ser usado para definir propriedades e chamar métodos. Por exemplo:

foo?.bar = Bar()

Novamente, nada de ruim vai acontecer aqui, se foofor nil. Seu código simplesmente continuará executando.

Outro truque interessante que o encadeamento opcional permite é verificar se a configuração de uma propriedade ou a chamada de um método foi bem-sucedida. Você pode fazer isso comparando o valor de retorno com nil.

(Isso ocorre porque um valor opcional retornará, Void?e não Voidem um método que não retorna nada)

Por exemplo:

if (foo?.bar = Bar()) != nil {
    print("bar was set successfully")
} else {
    print("bar wasn’t set successfully")
}

No entanto, as coisas se tornam um pouco mais complicadas ao tentar acessar propriedades ou chamar métodos que retornam um valor. Por fooser opcional, tudo o que for retornado também será opcional. Para lidar com isso, você pode desembrulhar os opcionais que são retornados usando um dos métodos acima - ou desembrulhar- foose antes de acessar métodos ou chamar métodos que retornam valores.

Além disso, como o nome sugere, você pode 'encadear' essas instruções juntas. Isso significa que se foopossui uma propriedade opcional baz, que possui uma propriedade qux- você pode escrever o seguinte:

let optionalQux = foo?.baz?.qux

Novamente, porque fooe bazsão opcionais, o valor retornado quxserá sempre opcional, independentemente de quxele próprio ser opcional.

map e flatMap

Um recurso frequentemente subutilizado com opcionais é a capacidade de usar as funções mape flatMap. Isso permite aplicar transformações não opcionais a variáveis ​​opcionais. Se um opcional tiver um valor, você poderá aplicar uma determinada transformação a ele. Se não tiver um valor, permanecerá nil.

Por exemplo, digamos que você tenha uma sequência opcional:

let anOptionalString:String?

Aplicando a mapfunção a ela - podemos usar a stringByAppendingStringfunção para concatená-la em outra string.

Como stringByAppendingStringusa um argumento de cadeia não opcional, não podemos inserir nossa cadeia opcional diretamente. No entanto, usando map, podemos usar allow stringByAppendingStringpara ser usado se anOptionalStringtiver um valor.

Por exemplo:

var anOptionalString:String? = "bar"

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

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

No entanto, se anOptionalStringnão tiver um valor, mapretornará nil. Por exemplo:

var anOptionalString:String?

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

print(anOptionalString) // nil

flatMapfunciona de maneira semelhante a map, exceto que permite retornar outro opcional de dentro do corpo do fechamento. Isso significa que você pode inserir uma opção opcional em um processo que requer uma entrada não opcional, mas pode gerar uma opção opcional.

try!

O sistema de tratamento de erros da Swift pode ser usado com segurança com o Do-Try-Catch :

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

Se someThrowingFunc()lançar um erro, o erro será capturado com segurança no catchbloco.

A errorconstante que você vê no catchbloco não foi declarada por nós - é gerada automaticamente por catch.

Você também pode errorse declarar , tem a vantagem de poder convertê-lo em um formato útil, por exemplo:

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

Usar tryesta maneira é a maneira correta de tentar, capturar e manipular erros provenientes de funções de lançamento.

Há também o try?que absorve o erro:

if let result = try? someThrowingFunc() {
    // cool
} else {
    // handle the failure, but there's no error information available
}

Mas o sistema de tratamento de erros da Swift também fornece uma maneira de "forçar a tentativa" com try!:

let result = try! someThrowingFunc()

Os conceitos explicados neste post também se aplicam aqui: se um erro for lançado, o aplicativo falhará.

Você só deve usar try!se puder provar que seu resultado nunca falhará no seu contexto - e isso é muito raro.

Na maioria das vezes, você usará o sistema Do-Try-Catch completo - e o opcional try?, nos raros casos em que o tratamento do erro não é importante.


Recursos

Hamish
fonte
87
Pessoalmente, gostaria de agradecer por se esforçar para escrever tudo isso. Eu sinto que isso certamente será útil para iniciantes e especialistas que usam o swift.
Pranav Wadhwa
15
Estou feliz que você tenha achado útil. Esta resposta é um wiki da comunidade, por isso é uma colaboração entre muitas pessoas (7 até agora)!
Jtbandes 06/06
1
Este erro é intermitente no meu caso. Alguma sugestão? Código em stackoverflow.com/questions/50933681/…
py_ios_dev
1
Talvez seja hora de atualizar esta resposta para usar em compactMap()vez de flatMap().
Nicolas Miari 19/09/18
então eu acho que a melhor e mais curta solução é o "Operador de coalescência zero", certo?
mehdigriche
65

Resposta TL; DR

Com muito poucas exceções , esta regra é dourada:

Evite o uso de !

Declarar variável opcional ( ?), não implicitamente desembrulhados (IUO) ( !)

Em outras palavras, use:
var nameOfDaughter: String?

Ao invés de:
var nameOfDaughter: String!

Desembrulhe a variável opcional usando if letouguard let

Desembrulhe a variável como esta:

if let nameOfDaughter = nameOfDaughter {
    print("My daughters name is: \(nameOfDaughter)")
}

Ou assim:

guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: \(nameOfDaughter)")

Esta resposta foi planejada para ser concisa, para uma compreensão completa, leia a resposta aceita


Recursos

Sajjon
fonte
40

Esta questão surge TODO O TEMPO no SO. É uma das primeiras coisas que os novos desenvolvedores do Swift enfrentam.

Fundo:

Swift usa o conceito de "Opcionais" para lidar com valores que poderiam conter um valor ou não. Em outros idiomas como C, você pode armazenar um valor 0 em uma variável para indicar que ela não contém valor. No entanto, e se 0 for um valor válido? Então você pode usar -1. E se -1 for um valor válido? E assim por diante.

Os opcionais Swift permitem configurar uma variável de qualquer tipo para conter um valor válido ou nenhum valor.

Você coloca um ponto de interrogação após o tipo quando declara uma variável como média (tipo x ou nenhum valor).

Um opcional é na verdade um contêiner que contém uma variável de um determinado tipo ou nada.

Um opcional precisa ser "desembrulhado" para buscar o valor interno.

O "!" O operador é um operador "forçar a desembrulhar". Ele diz "confie em mim. Eu sei o que estou fazendo. Garanto que, quando esse código for executado, a variável não conterá nada". Se você estiver errado, você trava.

A menos que você realmente não sabe o que está fazendo, evitar o "!" forçar desembrulhar o operador. É provavelmente a maior fonte de falhas para iniciantes em programadores Swift.

Como lidar com opcionais:

Existem muitas outras maneiras de lidar com opcionais que são mais seguros. Aqui estão alguns (não uma lista exaustiva)

Você pode usar "ligação opcional" ou "se deixar" dizer "se esta opção contiver um valor, salve esse valor em uma nova variável não opcional. Se a opção não contiver um valor, pule o corpo dessa instrução if "

Aqui está um exemplo de ligação opcional com o nosso fooopcional:

if let newFoo = foo //If let is called optional binding. {
  print("foo is not nil")
} else {
  print("foo is nil")
}

Observe que a variável que você define quando utiliza a opção opcional só existe (está apenas "no escopo") no corpo da instrução if.

Como alternativa, você pode usar uma declaração de guarda, que permite sair da sua função se a variável for nula:

func aFunc(foo: Int?) {
  guard let newFoo = input else { return }
  //For the rest of the function newFoo is a non-optional var
}

As instruções de guarda foram adicionadas no Swift 2. O Guard permite preservar o "caminho dourado" através do seu código e evitar níveis cada vez maiores de ifs aninhados que às vezes resultam do uso da ligação opcional "if let".

Há também uma construção chamada "operador coalescente nulo". Ele assume o formato "opcional_var ?? substituição_valor". Retorna uma variável não opcional com o mesmo tipo que os dados contidos no opcional. Se o opcional contiver nulo, ele retornará o valor da expressão após o "??" símbolo.

Então você pode usar código como este:

let newFoo = foo ?? "nil" // "??" is the nil coalescing operator
print("foo = \(newFoo)")

Você também pode usar a tentativa / captura ou guarda o tratamento de erros, mas geralmente uma das outras técnicas acima é mais limpa.

EDITAR:

Outra pegada um pouco mais sutil com os opcionais é "opcionais implicitamente desembrulhados. Quando declaramos foo, poderíamos dizer:

var foo: String!

Nesse caso, foo ainda é um opcional, mas você não precisa desembrulhá-lo para fazer referência a ele. Isso significa que sempre que você tenta fazer referência a foo, você falha se for nulo.

Portanto, este código:

var foo: String!


let upperFoo = foo.capitalizedString

Irá falhar com referência à propriedade capitalizedString de foo, mesmo que não sejamos forçados a desembrulhar. a impressão parece boa, mas não é.

Portanto, você quer ter muito cuidado com os opcionais implicitamente desembrulhados. (e talvez até evite-os completamente até que você tenha uma sólida compreensão dos opcionais.)

Conclusão: Quando você estiver aprendendo Swift pela primeira vez, finja o "!" O personagem não faz parte do idioma. É provável que você tenha problemas.

Duncan C
fonte
7
Você deve fazer deste um wiki público.
vacawama
3
Edite sua resposta e, abaixo da caixa de edição à direita, há uma caixa de seleção do wiki da comunidade . Você não receberá mais o representante pela resposta, mas sei que isso não é um flagrante flagrante de qualquer maneira.
vacawama
6
Como essa pergunta é feita um milhão de vezes no site, se eu postar uma pergunta auto-respondida como a resposta canônica da pergunta, espero que a resposta seja muito mais completa do que isso. Não há nada sobre guardcláusulas. Não há nada sobre o uso do if varqual seja uma construção tão válida quanto if let. Não há nada sobre wherecláusulas que eu acho que vale a pena mencionar quando estamos falando de if letligação (em muitos casos, remove uma camada inteira de aninhamento). Não há nada sobre encadeamento opcional.
Nhgrif 28/02
6
E quando você menciona opcionais implicitamente desembrulhados, nem mesmo menciona que pode usar todos os truques opcionais mencionados acima para desembrulhar com segurança os opcionais implicitamente desembrulhados. Também não cobrimos desembrulhar várias opções na mesma cláusula.
Nhgrif 28/02
1
@ nhgrif, eu disse "Você também pode usar a tentativa / captura ou guarda o tratamento de erros, mas geralmente uma das outras técnicas acima é mais limpa". Você certamente tem alguns bons pontos. Este é um tópico bastante amplo. Que tal contribuir com sua própria resposta que cubra coisas que eu não fiz, em vez de fazer comentários cortantes? Dessa forma, a pergunta e suas respostas se tornam um ativo melhor para o site.
Duncan C
13

Como as respostas acima explicam claramente como jogar com segurança com os opcionais. Vou tentar explicar o que os opcionais realmente são rápidos.

Outra maneira de declarar uma variável opcional é

var i : Optional<Int>

E o tipo opcional não passa de uma enumeração com dois casos, ou seja,

 enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none 
    case some(Wrapped)
    .
    .
    .
}

Então, atribuir um valor nulo à nossa variável 'i'. Podemos fazer var i = Optional<Int>.none ou atribuir um valor, passaremos algum valor var i = Optional<Int>.some(28)

Segundo swift, 'nil' é a ausência de valor. E para criar uma instância inicializada com nilTemos que nos conformar com um protocolo chamado ExpressibleByNilLiterale ótimo, se você adivinhou, apenas se Optionalsconformar ExpressibleByNilLiterale se conformar a outros tipos é desencorajado.

ExpressibleByNilLiteralpossui um único método chamado init(nilLiteral:)que inicializa um instace com zero. Você normalmente não chama esse método e, de acordo com a documentação rápida, é desencorajado chamar esse inicializador diretamente, como o compilador chama sempre que você inicializa um tipo opcional com nilliteral.

Até eu mesmo tenho que embrulhar (sem trocadilhos) minha cabeça em torno de Opcionais: D Feliz troca de todos .

Tharzeez
fonte
12

Primeiro, você deve saber o que é um valor opcional. Você pode ir para a linguagem de programação Swift para obter detalhes.

Segundo, você deve saber que o valor opcional possui dois status. Um é o valor total e o outro é um valor nulo. Portanto, antes de implementar um valor opcional, você deve verificar qual é o estado.

Você pode usar if let ...ou guard let ... elsee assim por diante.

Por outro lado, se você não deseja verificar o estado da variável antes da sua implementação, também pode usar var buildingName = buildingName ?? "buildingName".

QiunCheng
fonte
8

Eu tive esse erro uma vez quando estava tentando definir meus valores de Outlets no método prepare for segue da seguinte maneira:

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

Descobri que não consigo definir os valores das tomadas do controlador de destino porque o controlador ainda não foi carregado ou inicializado.

Então eu resolvi desta maneira:

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 resposta ajude alguém com o mesmo problema, pois achei que a resposta marcada é um grande recurso para a compreensão dos opcionais e como eles funcionam, mas não abordou o problema diretamente.

Wissa
fonte
Só não se esqueça de mencionar onde a chamada updateViewno controlador de destino;)
Ahmad F
@AhmadF good point. No entanto, updateViewnão é necessário chamar o controlador de destino neste caso, pois estou usando a namevariável para definir nameLabel.texto viewDidLoad. Mas se estivéssemos fazendo muitas configurações, certamente seria melhor criar outra função e chamá-la a partir do viewDidLoadlugar.
Wissa 27/02/19
7

Basicamente, você tentou usar um valor nulo em locais onde o Swift permite apenas valores nulos, dizendo ao compilador para confiar em você que nunca haverá valor nulo, permitindo que o aplicativo seja compilado.

Existem vários cenários que levam a esse tipo de erro fatal:

  1. desembrulhar forçado:

    let user = someVariable!

    Se someVariablefor nulo, você receberá uma falha. Fazendo um desembrulhamento forçado, você transferiu a responsabilidade de verificação nula do compilador para você, basicamente, fazendo um desembrulhamento forçado que você garante ao compilador que nunca terá valores nulos lá. E adivinhe o que acontece se, de alguma forma, um valor nulo terminar someVariable?

    Solução? Use a ligação opcional (também conhecida como if-let), faça o processamento da variável:

    if user = someVariable {
        // do your stuff
    }
  2. lançamentos forçados (para baixo):

    let myRectangle = someShape as! Rectangle

    Aqui, por fundição forçada, você diz ao compilador para não se preocupar mais, pois sempre haverá uma Rectangleinstância lá. E enquanto isso acontecer, você não precisa se preocupar. Os problemas começam quando você ou seus colegas do projeto começam a circular valores não retangulares.

    Solução? Use a ligação opcional (também conhecida como if-let), faça o processamento da variável:

    if let myRectangle = someShape as? Rectangle {
        // yay, I have a rectangle
    }
  3. Opcionais implicitamente desembrulhados. Vamos supor que você tenha a seguinte definição de classe:

    class User {
        var name: String!
    
        init() {
            name = "(unnamed)"
        }
    
        func nicerName() {
            return "Mr/Ms " + name
        }
    }

    Agora, se ninguém mexer com a namepropriedade configurando-a como nil, ela funcionará conforme o esperado; no entanto, se Userfor inicializado a partir de um JSON que não possui a namechave, você receberá o erro fatal ao tentar usar a propriedade.

    Solução? Não os use :) A menos que você tenha 102% de certeza de que a propriedade sempre terá um valor não nulo quando precisar ser usada. Na maioria dos casos, a conversão para um opcional ou não opcional funcionará. Torná-lo não opcional também resultará no compilador, ajudando-o a informar os caminhos de código que você perdeu, fornecendo um valor a essa propriedade

  4. Tomadas desconectadas ou ainda não conectadas. Este é um caso particular do cenário 3. Basicamente, você tem alguma classe carregada em XIB que deseja usar.

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

    Agora, se você perdeu a conexão da tomada a partir do editor XIB, o aplicativo falhará assim que você desejar usá-la. Solução? Verifique se todas as tomadas estão conectadas. Ou use o ?operador sobre eles: emailTextField?.text = "[email protected]". Ou declare a tomada como opcional, embora, neste caso, o compilador o force a desembrulhá-lo por todo o código.

  5. Valores provenientes de Objective-C e que não possuem anotações de nulidade. Vamos supor que temos a seguinte classe Objective-C:

    @interface MyUser: NSObject
    @property NSString *name;
    @end

    Agora, se nenhuma anotação de nulidade for especificada (explicitamente ou via NS_ASSUME_NONNULL_BEGIN/ NS_ASSUME_NONNULL_END), a namepropriedade será importada no Swift como String!(um IUO - opcional implicitamente desembrulhado). Assim que algum código rápido quiser usar o valor, ele travará se namefor nulo.

    Solução? Adicione anotações de nulidade ao seu código do Objective-C. Cuidado, porém, o compilador Objective-C é um pouco permissivo quando se trata de nulidade; você pode acabar com valores nulos, mesmo se os tiver marcado explicitamente como nonnull.

Cristik
fonte
2

Esse é um comentário mais importante e é por isso que os opcionais implicitamente desembrulhados podem ser enganosos quando se trata de nilvalores de depuração .

Pense no seguinte código: Ele é compilado sem erros / avisos:

c1.address.city = c3.address.city

No entanto, em tempo de execução, ele fornece o seguinte erro: Erro fatal: inesperadamente encontrado nulo ao desembrulhar um valor opcional

Você pode me dizer qual é o objeto nil?

Você não pode!

O código completo seria:

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
}

Para encurtar a história, usando var address : Address!você está ocultando a possibilidade de uma variável ser nilde outros leitores. E quando trava você fica tipo "que diabos ?! meu addressnão é opcional, então por que estou travando?!.

Por isso, é melhor escrever da seguinte maneira:

c1.address.city = c2.address!.city  // ERROR:  Fatal error: Unexpectedly found nil while unwrapping an Optional value 

Agora você pode me dizer que objeto é esse nil?

Desta vez, o código ficou mais claro para você. Você pode racionalizar e pensar que provavelmente esse é o addressparâmetro que foi forçado a ser desembrulhado.

O código completo seria:

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
}
Mel
fonte
2

Os erros EXC_BAD_INSTRUCTIONe fatal error: unexpectedly found nil while implicitly unwrapping an Optional valueaparecem mais quando você declara um @IBOutlet, mas não está conectado ao storyboard .

Você também deve aprender sobre como os Opcionais funcionam, mencionados em outras respostas, mas esta é a única vez que me aparece principalmente.

Ale Mohamad
fonte
a @IBOutletcausa desse erro não deve ter o erro Fatal: Inesperadamente encontrado nulo ao desembrulhar implicitamente uma versão de valor opcional do erro?
Pkamb # 8/19
1
Isso é verdade. Talvez quando eu enviar a resposta, foi isso que eu quis dizer e copiei a primeira mensagem de erro fatal. A resposta de Hamish parece muito completa sobre isso.
Ale Mohamad
2

Se você receber esse erro no CollectionView, tente criar o arquivo CustomCell e o Custom xib também.

adicione esse código em ViewDidLoad () em mainVC.

    let nib = UINib(nibName: "CustomnibName", bundle: nil)
    self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")
ShigaSuresh
fonte
0

Me deparei com esse erro ao fazer uma transição de um controlador de exibição de tabela para um controlador de exibição porque esqueci de especificar o nome da classe personalizada para o controlador de exibição no storyboard principal.

Algo simples que vale a pena conferir se tudo parece bem

Mike Volmar
fonte