O programa My Swift está causando EXC_BAD_INSTRUCTION
um 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.
swift
exception
error-handling
pkamb
fonte
fonte
Respostas:
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-1
ser usado para indicar a ausência de um número inteiro, ou talvezINT_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:A falta de um valor em um opcional é indicada por
nil
:(Observe que isso
nil
não é o mesmo quenil
no 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.
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:Como
anOptionalString
estánil
aqui, 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.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á.
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
nil
em 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
.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 {...}
ouif var x = anOptional {...}
, dependendo se você precisar modificar o valor da nova variável após vinculá-la.Por exemplo:
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.
Outro truque interessante é que você também pode usar vírgulas para verificar uma determinada condição no valor, depois de desembrulhá-lo.
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:
(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á desempacotado e atribuído à novanumber
constante. 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 só 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:
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
where
clá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
, ondea
é um tipo opcional eb
é do mesmo tipoa
(embora geralmente não seja opcional).Essencialmente, permite que você diga “Se
a
contém um valor, desembrulhe-o. Se não, então retorneb
”. Por exemplo, você pode usá-lo assim:Isso definirá uma
number
constante doInt
tipo, que conterá o valor deanOptionalInt
, se ele contiver um valor ou0
outro.É apenas uma abreviação para:
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 umaFoo
instância opcional .Se quisermos chamar um método
foo
que não retorne nada, podemos simplesmente fazer:Se
foo
contiver 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
nil
no Objective-C)Portanto, isso também pode ser usado para definir propriedades e chamar métodos. Por exemplo:
Novamente, nada de ruim vai acontecer aqui, se
foo
fornil
. 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ãoVoid
em um método que não retorna nada)Por exemplo:
No entanto, as coisas se tornam um pouco mais complicadas ao tentar acessar propriedades ou chamar métodos que retornam um valor. Por
foo
ser 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-foo
se 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
foo
possui uma propriedade opcionalbaz
, que possui uma propriedadequx
- você pode escrever o seguinte:Novamente, porque
foo
ebaz
são opcionais, o valor retornadoqux
será sempre opcional, independentemente dequx
ele próprio ser opcional.map
eflatMap
Um recurso frequentemente subutilizado com opcionais é a capacidade de usar as funções
map
eflatMap
. 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:
Aplicando a
map
função a ela - podemos usar astringByAppendingString
função para concatená-la em outra string.Como
stringByAppendingString
usa um argumento de cadeia não opcional, não podemos inserir nossa cadeia opcional diretamente. No entanto, usandomap
, podemos usar allowstringByAppendingString
para ser usado seanOptionalString
tiver um valor.Por exemplo:
No entanto, se
anOptionalString
não tiver um valor,map
retornaránil
. Por exemplo:flatMap
funciona de maneira semelhante amap
, 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 :
Se
someThrowingFunc()
lançar um erro, o erro será capturado com segurança nocatch
bloco.A
error
constante que você vê nocatch
bloco não foi declarada por nós - é gerada automaticamente porcatch
.Você também pode
error
se declarar , tem a vantagem de poder convertê-lo em um formato útil, por exemplo:Usar
try
esta 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:Mas o sistema de tratamento de erros da Swift também fornece uma maneira de "forçar a tentativa" com
try!
: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
fonte
compactMap()
vez deflatMap()
.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 let
ouguard let
Desembrulhe a variável como esta:
Ou assim:
Esta resposta foi planejada para ser concisa, para uma compreensão completa, leia a resposta aceita
Recursos
fonte
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
foo
opcional: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:
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:
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:
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:
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.
fonte
guard
cláusulas. Não há nada sobre o uso doif var
qual seja uma construção tão válida quantoif let
. Não há nada sobrewhere
cláusulas que eu acho que vale a pena mencionar quando estamos falando deif let
ligação (em muitos casos, remove uma camada inteira de aninhamento). Não há nada sobre encadeamento opcional.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,
Então, atribuir um valor nulo à nossa variável 'i'. Podemos fazer
var i = Optional<Int>.none
ou atribuir um valor, passaremos algum valorvar i = Optional<Int>.some(28)
Segundo swift, 'nil' é a ausência de valor. E para criar uma instância inicializada com
nil
Temos que nos conformar com um protocolo chamadoExpressibleByNilLiteral
e ótimo, se você adivinhou, apenas seOptionals
conformarExpressibleByNilLiteral
e se conformar a outros tipos é desencorajado.ExpressibleByNilLiteral
possui um único método chamadoinit(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 comnil
literal.Até eu mesmo tenho que embrulhar (sem trocadilhos) minha cabeça em torno de Opcionais: D Feliz troca de todos .
fonte
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 ...
ouguard let ... else
e 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"
.fonte
Eu tive esse erro uma vez quando estava tentando definir meus valores de Outlets no método prepare for segue da seguinte maneira:
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:
Controlador de destino:
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.
fonte
updateView
no controlador de destino;)updateView
não é necessário chamar o controlador de destino neste caso, pois estou usando aname
variável para definirnameLabel.text
oviewDidLoad
. Mas se estivéssemos fazendo muitas configurações, certamente seria melhor criar outra função e chamá-la a partir doviewDidLoad
lugar.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:
desembrulhar forçado:
Se
someVariable
for 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 terminarsomeVariable
?Solução? Use a ligação opcional (também conhecida como if-let), faça o processamento da variável:
lançamentos forçados (para baixo):
Aqui, por fundição forçada, você diz ao compilador para não se preocupar mais, pois sempre haverá uma
Rectangle
instâ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:
Opcionais implicitamente desembrulhados. Vamos supor que você tenha a seguinte definição de classe:
Agora, se ninguém mexer com a
name
propriedade configurando-a comonil
, ela funcionará conforme o esperado; no entanto, seUser
for inicializado a partir de um JSON que não possui aname
chave, 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
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.
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.Valores provenientes de Objective-C e que não possuem anotações de nulidade. Vamos supor que temos a seguinte classe Objective-C:
Agora, se nenhuma anotação de nulidade for especificada (explicitamente ou via
NS_ASSUME_NONNULL_BEGIN
/NS_ASSUME_NONNULL_END
), aname
propriedade será importada no Swift comoString!
(um IUO - opcional implicitamente desembrulhado). Assim que algum código rápido quiser usar o valor, ele travará sename
for 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
.fonte
Esse é um comentário mais importante e é por isso que os opcionais implicitamente desembrulhados podem ser enganosos quando se trata de
nil
valores de depuração .Pense no seguinte código: Ele é compilado sem erros / avisos:
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:
Para encurtar a história, usando
var address : Address!
você está ocultando a possibilidade de uma variável sernil
de outros leitores. E quando trava você fica tipo "que diabos ?! meuaddress
não é opcional, então por que estou travando?!.Por isso, é melhor escrever da seguinte maneira:
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
address
parâmetro que foi forçado a ser desembrulhado.O código completo seria:
fonte
Os erros
EXC_BAD_INSTRUCTION
efatal error: unexpectedly found nil while implicitly unwrapping an Optional value
aparecem 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.
fonte
@IBOutlet
causa desse erro não deve ter o erro Fatal: Inesperadamente encontrado nulo ao desembrulhar implicitamente uma versão de valor opcional do erro?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.
fonte
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
fonte