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

Meu programa Swift está falhando com EXC_BAD_INSTRUCTION e esse erro. O que significa e como faço para corrigir isso?

erro fatal: inesperadamente encontrado nulo ao desembrulhar um valor opcional


Esta postagem destina-se a coletar respostas para problemas “inesperadamente encontrados nulos”, 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 do wiki existente.

Esta resposta é wiki da comunidade . Se você acha que poderia ser melhor, sinta-se à vontade para editá-lo !

Antecedentes: o que é um 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 determinado valor “sentinela” é freqüentemente usado para indicar a falta de um valor . Em Objective-C, por exemplo, nil (o ponteiro nulo ) indica a falta de um object. Mas isso fica mais complicado quando se trabalha com tipos primitivos – deve -1 ser usado para indicar a ausência de um inteiro, ou talvez INT_MIN , ou algum outro inteiro? Se qualquer valor específico for escolhido para significar “no integer”, isso significa que ele não pode mais ser tratado como um valor válido .

O Swift é uma linguagem com segurança de tipos, o que significa que a linguagem ajuda você a ser claro sobre os tipos de valores com os quais seu código pode trabalhar. Se parte do seu código espera uma String, digite security para evitar que você passe uma Int por engano.

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

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 

(Note que este nil não é o mesmo que o nil em Objective-C. Em Objective-C, nil é a ausência de um ponteiro de object válido; no Swift, Optionals não estão restritos a objects / tipos de referência. Opcional comporta-se de forma semelhante ao Haskell Talvez .)


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

Para acessar um valor opcional (se tiver algum), é necessário desembrulhá- lo. Um valor opcional pode ser desembrulhado de forma segura ou forçada. 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 batida

Esse travamento pode ocorrer com dois tipos diferentes de força-desembrulhar:

1. Desempacotamento Explícito de Força

Isso é feito com o ! operador em um opcional. Por exemplo:

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

Como anOptionalString é nil aqui, você terá uma falha na linha onde você o força a desembrulhar.

2. Opcionais implicitamente não-embalados

Estes são definidos com um ! em vez de um ? 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 não desembrulhado, ele será automaticamente desdobrado para você. Se não contiver um valor, ele falhará.

 print(optionalDouble) // <- CRASH 

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

Os IBOutlets, em particular, são geralmente opcionais implicitamente não desembrulhados. Isso ocorre porque seu xib ou storyboard ligará as saídas no tempo de execução, após a boot. Portanto, você deve garantir que não esteja acessando as tomadas antes que elas sejam carregadas. Você também deve verificar se as conexões estão corretas em seu arquivo storyboard / xib, caso contrário, os valores serão nil no tempo de execução e, portanto, falharão quando estiverem implicitamente desembrulhado.


Quando devo forçar o Desembrulhar um Opcional?

Força de Desembrulhar Explícito

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

Embora possa haver uma ocasião em que você possa usar o desembrulhamento de força, como você sabe, um opcional contém um valor - não há um único local em que você não possa desembrulhar esse opcional em segurança.

Opcionais implicitamente não-embalados

Essas variables ​​são projetadas para que você possa adiar sua atribuição até mais tarde em seu código. É sua responsabilidade garantir que eles tenham um valor antes de você acessá-los. No entanto, como envolvem desdobramento de força, eles ainda são inerentemente inseguros - pois assumem que seu valor não é nulo, mesmo que atribuir nulo seja válido.

Você só deve estar usando opcionalmente opcionalmente desembrulhados como último recurso . Se você pode usar uma variável lenta , ou fornecer um valor padrão para uma variável - você deve fazê-lo em vez de usar um opcional implicitamente não-desembrulhado.

No entanto, existem alguns cenários em que opcionais implicitamente não 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 caucanvas.


Como posso lidar com segurança com as opções?

A maneira mais simples de verificar se um opcional contém um valor é compará-lo a 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, você realmente deseja acessar o valor que ele contém, se ele contiver um. Para fazer isso, você pode usar a binding opcional .

Ligação Opcional

A binding opcional permite verificar se um opcional contém um valor - e permite atribuir o valor não desembrulhado a uma nova variável ou constante. Ele usa a syntax if let x = anOptional {...} ou if var x = anOptional {...} , dependendo se você precisar modificar o valor da nova variável depois de vinculá-la.

Por exemplo:

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

O que isto faz é primeiro verificar se o opcional contém um valor. Em caso afirmativo, 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á chamada, como você esperaria.

O que é legal sobre a binding opcional, é você pode desembrulhar vários opcionais ao mesmo tempo. Você pode simplesmente separar as instruções com uma vírgula. A declaração será bem-sucedida 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 em usar a binding opcional em uma instrução if é que você só pode acessar o valor desembrulhado dentro do escopo da instrução. Se você precisar acessar o valor fora do escopo da instrução, poderá usar uma instrução de guarda .

Uma declaração de guarda permite que você defina uma condição para o sucesso - e o escopo atual só continuará executando se essa condição for atendida. Eles são definidos com a guard condition else {...} syntax guard condition else {...} .

Então, para usá-los com uma binding opcional, você pode fazer isso:

 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 anOptionalInt contiver um valor, ele será desembrulhado e atribuído à nova constante number . O código depois da guarda continuará então a executar. Se não contiver um valor - o guarda executará o código dentro dos parênteses, o que levará à transferência de controle, para que o código imediatamente após não seja executado.

A coisa mais interessante sobre as instruções de guarda é que o valor desembrulhado agora está disponível para uso no código que segue a instrução (como sabemos que o código futuro pode ser executado se o opcional tiver um valor). Isso é ótimo para eliminar 'pirâmides do destino' criadas pelo aninhamento de 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 cláusula where .

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

Operador Coalescente Nulo

O Nil Coalescing Operator é uma versão abreviada bacana do operador condicional ternário , projetado principalmente para converter opcionais em não-opcionais. Tem a syntax a ?? b a ?? b , onde a é um tipo opcional b é do mesmo tipo que a (embora geralmente não opcional).

Essencialmente, permite que você diga “Se a contém um valor, desdobre-o. Se não, então retorne b vez disso ”. Por exemplo, você poderia usá-lo assim:

 let number = anOptionalInt ?? 0 

Isso definirá uma constante number do tipo Int , que conterá o valor de anOptionalInt , se ele contiver um valor, ou 0 caso contrário.

É apenas um atalho 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 sufixando o nome da variável com um ? quando usá-lo.

Por exemplo, digamos que temos uma variável foo , de tipo uma instância Foo opcional.

 var foo : Foo? 

Se quisermos chamar um método em foo que não retorna nada, podemos simplesmente fazer:

 foo?.doSomethingInteresting() 

Se foo contiver um valor, esse método será chamado. Se isso não acontecer, nada de ruim vai acontecer - o código simplesmente continuará executando.

(Este é um comportamento similar ao envio de mensagens para nil em Objective-C)

Portanto, isso também pode ser usado para definir propriedades e também methods de chamada. Por exemplo:

 foo?.bar = Bar() 

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

Outro truque que o encadeamento opcional permite fazer é 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 a nil .

(Isso ocorre porque um valor opcional retornará Void? vez de Void em 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 methods que retornam um valor. Porque foo é opcional, qualquer coisa retornada dele também será opcional. Para lidar com isso, você pode desembrulhar os opcionais que são retornados usando um dos methods acima - ou desembrulhar o próprio foo antes de acessar methods ou chamar methods que retornam valores.

Além disso, como o nome sugere, você pode "encadear" essas instruções juntas. Isto significa que se foo tem uma propriedade opcional baz , que tem uma propriedade qux - você poderia escrever o seguinte:

 let optionalQux = foo?.baz?.qux 

Novamente, como foo e baz são opcionais, o valor retornado de qux sempre será opcional, independentemente de o próprio qux ser opcional.

map e flatMap

Um recurso frequentemente subutilizado com opcionais é a capacidade de usar as funções map e flatMap . Isso permite que você aplique transformações não opcionais a variables ​​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 string opcional:

 let anOptionalString:String? 

Aplicando a function do map a ela, podemos usar a function stringByAppendingString para concatená-la com outra string.

Como stringByAppendingString usa um argumento de string não opcional, não podemos inserir nossa string opcional diretamente. No entanto, usando map , podemos usar allow stringByAppendingString para ser usado se anOptionalString tiver um valor.

Por exemplo:

 var anOptionalString:String? = "bar" anOptionalString = anOptionalString.map {unwrappedString in return "foo".stringByAppendingString(unwrappedString) } print(anOptionalString) // Optional("foobar") 

No entanto, se anOptionalString não tiver um valor, o map retornará nil . Por exemplo:

 var anOptionalString:String? anOptionalString = anOptionalString.map {unwrappedString in return "foo".stringByAppendingString(unwrappedString) } print(anOptionalString) // nil 

flatMap funciona de forma semelhante ao map , exceto que permite retornar outro opcional a partir do corpo do fechamento. Isso significa que você pode inserir um opcional em um processo que requer uma input não opcional, mas pode produzir um opcional opcional.

try!

O sistema de tratamento de erros do Swift pode ser usado com segurança com 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 bloco catch .

A constante de error você vê no bloco catch não foi declarada por nós - é gerada automaticamente pelo catch .

Você também pode declarar error , 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 try dessa maneira é a maneira correta de tentar, capturar e manipular erros provenientes de funções de lançamento.

Há também 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 manipulação de erros do Swift também oferece uma maneira de "forçar a tentativa" com o try! :

 let result = try! someThrowingFunc() 

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

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

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


Recursos

  • Documentação da Apple no Swift Optionals
  • Quando usar e quando não usar os opcionais implicitamente não desembrulhados
  • Aprenda como depurar um acidente com o aplicativo iOS

TL; resposta DR

Com pouquíssimas exceções , essa regra é de ouro:

Evite o uso de !

Declare a variável opcional ( ? ), E não implicitamente os opcionais não desembrulhados (IUO) ( ! )

Em outras palavras, prefiro usar:
var nameOfDaughter: String?

Ao invés de:
var nameOfDaughter: String!

Desembrulhe a variável opcional usando if let ou guard let

Desdobrar variável assim:

 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 destinada a ser concisa, para a compreensão completa, leia a resposta aceita

Esta questão surge TODA A VEZ em SO. É uma das primeiras coisas que os novos desenvolvedores do Swift lutam.

Fundo:

O Swift usa o conceito de “Opcionais” para lidar com valores que podem conter um valor ou não. Em outras linguagens como C, você pode armazenar um valor 0 em uma variável para indicar que não contém nenhum 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 ao declarar uma variável como significando (digite 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 “!” operador é um operador de “desdobrar a força”. 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ê está errado, você falha.

A menos que você realmente saiba o que está fazendo, evite o “!” force desembrulhar o operador. É provavelmente a maior fonte de falhas para os programadores iniciais do Swift.

Como lidar com os opcionais:

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

Você pode usar “binding opcional” ou “se deixar” para dizer “se este opcional contiver um valor, salve esse valor em uma nova variável não opcional. Se o opcional não contiver um valor, pule o corpo desta instrução if “.

Aqui está um exemplo de binding opcional com o nosso foo opcional:

 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 usa o opcional biding existe apenas (é apenas “no escopo”) no corpo da instrução if.

Como alternativa, você poderia usar uma instrução de guarda, que permite que você saia da sua function 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 } 

Declarações de guarda foram adicionadas no Swift 2. O Guard permite preservar o “caminho dourado” através de seu código e evitar níveis cada vez maiores de ifs nesteds que às vezes resultam do uso da binding opcional “se houver”.

Há também um constructo chamado “operador de coalescência nula”. Ele toma o formato “optional_var ?? replacement_val”. Ele retorna uma variável não opcional com o mesmo tipo que os dados contidos no opcional. Se o opcional contiver nil, retorna o valor da expressão após o “??” símbolo.

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

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

Você também pode usar o tratamento de erro try / catch ou guard, mas geralmente uma das outras técnicas acima é mais limpa.

EDITAR:

Outra pegadinha um pouco mais sutil com opcionais é “opcionalmente desdobrada. Quando declaramos foo, podemos dizer:

 var foo: String! 

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

Então esse código:

 var foo: String! let upperFoo = foo.capitalizedString 

Irá falhar com referência à propriedade capitalizedString do foo, mesmo que não tenhamos que desembrulhar a força foo. a impressão parece bem, mas não é.

Assim, você quer ter muito cuidado com opções implicitamente não-desembrulhadas. (e talvez até mesmo evitá-los completamente até que você tenha uma sólida compreensão dos opcionais).

Resumindo: quando você estiver aprendendo Swift pela primeira vez, finja o “!” personagem não faz parte da linguagem. É provável que você tenha problemas.

Primeiro, você deve saber o que é um valor opcional. Você pode ir para o The Swift Programming Launage

para detalhes.

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

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

Uma outra maneira, se você não quiser verificar seu estado antes do seu implemento, você também pode usar var buildingName = buildingName ?? "buildingName" var buildingName = buildingName ?? "buildingName" vez disso.

Já que as respostas acima explicam claramente como jogar com segurança com o Optionals. Vou tentar explicar o que as opções são realmente rápidas.

Outra maneira de declarar uma variável opcional é

var i : Optional

E tipo opcional não é nada, mas uma enumeração com dois casos, ou seja,

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

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

De acordo com a rapidez, ‘zero’ é a ausência de valor. E para criar uma instância inicializada com nil temos que estar em conformidade com um protocolo chamado ExpressibleByNilLiteral e ótimo se você adivinhou, apenas Optionals conformidade com ExpressibleByNilLiteral e em conformidade com outros tipos é desencorajado.

ExpressibleByNilLiteral tem um único método chamado init(nilLiteral:) que inicializa um instace com nil. Você normalmente não chamará esse método e, de acordo com a documentação rápida, não é recomendado chamar esse inicializador diretamente, pois o compilador o chama sempre que você inicializa um tipo Opcional com literal nil .

Mesmo eu tenho que embrulhar (sem trocadilhos) minha cabeça em volta de Optionals: D Happy Swfting All .

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

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

Então descobri que não posso definir os valores das saídas do controlador de destino porque o controlador ainda não foi carregado ou inicializado.

Então resolvi assim:

 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 diretamente o problema em si.

erro fatal: inesperadamente encontrado nulo ao desembrulhar um valor opcional

Ele diz que você está usando a variável cujo valor não é inicializado ou definido como nulo.

Por exemplo

 var tempVar: String? print(tempVar!) 

tempVar não é inicializado, então o aplicativo trava nesse caso, então você tem que usar

 print(tempVar ?? "") 

Por favor, consulte Encadeamento opcional para mais informações detalhadas