O que significa “Erro fatal: inesperadamente encontrado nulo ao desembrulhar um valor opcional”?
Meu programa Swift está travando com EXC_BAD_INSTRUCTION
um dos seguintes erros semelhantes. O que esse erro significa e como faço para corrigi-lo?
Erro fatal: inesperadamente encontrado nulo ao desempacotar um valor opcional
ou
Erro fatal: inesperadamente encontrado nulo ao desempacotar implicitamente um valor opcional
Esta postagem tem como objetivo coletar respostas para questões "inesperadamente encontradas nulas", para que não fiquem espalhadas e sejam difíceis de encontrar. Sinta-se à vontade para adicionar sua própria resposta ou editar a resposta existente no wiki.
Respostas
Esta resposta é wiki da comunidade . Se você acha que poderia ser melhorado, fique à vontade para editá-lo !
Histórico: o que é um opcional?
Em Swift, Optional<Wrapped>
é um tipo de opção : pode conter qualquer valor do tipo original ("Wrapped") ou nenhum valor (o valor especial nil
). Um valor opcional deve ser desembrulhado antes de ser usado.
Opcional é um tipo genérico , o que significa que Optional<Int>
e Optional<String>
são tipos distintos - o tipo dentro <>
é chamado de tipo Wrapped. Nos bastidores, um opcional é um enum com dois casos: .some(Wrapped)
e .none
, onde .none
é equivalente a nil
.
Opcionais podem ser declarados usando o tipo nomeado Optional<T>
, ou (mais comumente) como uma abreviação com um ?
sufixo.
var anInt: Int = 42
var anOptionalInt: Int? = 42
var anotherOptionalInt: Int? // `nil` is the default when no value is provided
var aVerboseOptionalInt: Optional<Int> // equivalent to `Int?`
anOptionalInt = nil // now this variable contains nil instead of an integer
Opcionais são uma ferramenta simples, mas poderosa, para expressar suas suposições ao escrever código. O compilador pode usar essas informações para evitar que você cometa erros. Da linguagem de programação Swift :
Swift é uma linguagem de tipo seguro , 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 de seu código exigir um
String
, a segurança de tipo evita que você o transmitaInt
por engano. Da mesma forma, a segurança de tipo evita que você passe acidentalmente um opcionalString
para um trecho de código que requer um não opcionalString
. A segurança de tipo ajuda a detectar e corrigir erros o mais cedo possível no processo de desenvolvimento.
Algumas outras linguagens de programação também têm tipos de opção genéricos : por exemplo, Maybe in Haskell, option in Rust e opcional em C ++ 17.
Em linguagens de programação sem tipos de opção, um valor de "sentinela" particular é freqüentemente usado para indicar a ausência de um valor válido. Em Objective-C, por exemplo, nil
(o ponteiro nulo ) representa a falta de um objeto. Para tipos primitivos como int
, um ponteiro nulo não pode ser usado, então você precisaria de uma variável separada (como value: Int
e isValid: Bool
) ou um valor de sentinela designado (como -1
ou INT_MIN
). Essas abordagens estão sujeitas a erros porque é fácil esquecer de verificar isValid
ou verificar o valor sentinela. Além disso, se um determinado valor for escolhido como sentinela, isso significa que ele não pode mais ser tratado como um valor válido .
Tipos de opções como o do Swift Optional
resolvem esses problemas introduzindo um nil
valor especial separado (para que você não precise designar um valor sentinela) e aproveitando o sistema de tipo forte para que o compilador possa ajudá-lo a se lembrar de verificar se há nulo quando necessário.
Por que recebi “ erro fatal: inesperadamente encontrado nulo ao desembrulhar um valor opcional ”?
Para acessar um valor opcional (se houver algum), 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 irá travar com a mensagem acima.
O Xcode mostrará a falha destacando uma linha de código. O problema ocorre nesta linha.
Esta falha pode ocorrer com dois tipos diferentes de desempacotamento forçado:
1. Força explícita de desempacotamento
Isso é feito com o !
operador em um opcional. Por exemplo:
let anOptionalString: String?
print(anOptionalString!) // <- CRASH
Erro fatal: inesperadamente encontrado nulo ao desempacotar um valor opcional
Como anOptionalString
está nil
aqui, você terá uma falha na linha onde forçá-lo a desembrulhar.
2. Opcionais Implicitamente Não Embalados
Eles 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 desembrulhado implicitamente, ele será automaticamente desembrulhado à força para você. Se não contiver um valor, ele irá travar.
print(optionalDouble) // <- CRASH
Erro fatal: inesperadamente encontrado nulo ao desempacotar implicitamente um valor opcional
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.
IBOutlets, em particular, são geralmente opcionais implicitamente desembrulhados. Isso ocorre porque seu xib ou storyboard vinculará as saídas em tempo de execução, após a inicialização. Portanto, você deve garantir que não está acessando as tomadas antes de serem carregadas. Você também deve verificar se as conexões estão corretas em seu arquivo de storyboard / xib, caso contrário, os valores estarão nil
em tempo de execução e, portanto, falharão quando forem desembrulhados implicitamente . Ao consertar as conexões, tente excluir as linhas de código que definem suas saídas e reconecte-as.
Quando devo forçar o desembrulhar um opcional?
Explicit Force Unwrapping
Como regra geral, você nunca deve forçar explicitamente o 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.
Embora possa haver uma ocasião em que você possa usar o desembrulhamento forçado, como você sabe com certeza que um opcional contém um valor - não há um único lugar onde você não possa desembrulhar esse opcional com segurança.
Opcionais implicitamente não embrulhados
Essas variáveis são projetadas para que você possa adiar sua atribuição para mais tarde em seu código. É sua responsabilidade garantir que eles tenham um valor antes de acessá-los. No entanto, como eles envolvem o desembrulhamento forçado, eles ainda são inerentemente inseguros - já que presumem que seu valor não é nulo, embora atribuir nulo seja válido.
Você só deve usar opcionais não embalados implicitamente como último recurso . Se você pode usar uma variável preguiçosa , ou fornecer um valor padrão para uma variável - você deve fazer isso ao invés de usar um opcional desdobrado implicitamente.
No entanto, existem alguns cenários em que opcionais desembrulhados implicitamente 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 o devido cuidado.
Como posso lidar com segurança com 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% das vezes ao trabalhar com opcionais, você realmente desejará acessar o valor que ele contém, se ele contiver algum. Para fazer isso, você pode usar Vinculação opcional .
Ligação opcional
A Ligação opcional permite que você verifique se um opcional contém um valor - e permite que você atribua o valor não encapsulado 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 ele faz , então o valor 'desembrulhado' é atribuído a uma nova variável ( number
) - que você pode usar livremente como se fosse não opcional. Se o opcional não contiver um valor, a cláusula else será invocada, como você esperaria.
O que é legal sobre a vinculação opcional é que você pode desembrulhar vários opcionais ao mesmo tempo. Você pode apenas separar as declarações com uma vírgula. A instrução terá sucesso 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 se há uma determinada condição no valor, após 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 não empacotado dentro do escopo da instrução. Se você precisar acessar o valor de fora do escopo da instrução, poderá usar uma instrução guard .
Uma instrução de guarda permite definir uma condição de sucesso - e o escopo atual só continuará em execução 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 anOptionalInt
contiver um valor, ele será desembrulhado e atribuído à nova number
constante. O código após o guarda continuará em execução. Se não contiver um valor - o guarda executará o código entre colchetes, o que levará à transferência de controle, para que o código imediatamente após não seja executado.
O que é realmente legal sobre as instruções de guarda é que o valor não empacotado agora está disponível para uso no código que segue a instrução (como sabemos que o código futuro só pode ser executado se o opcional tiver um valor). Isso é ótimo para eliminar 'pirâmides da desgraça' 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 legais que a instrução if suportava, como desembrulhar vários opcionais ao mesmo tempo e usar a where
cláusula.
O uso de uma instrução if ou guard depende completamente se algum código futuro requer que o opcional contenha um valor.
Operador de coalescência zero
O operador de coalescência nula é uma versão abreviada bacana do operador condicional ternário , projetado principalmente para converter opcionais em não opcionais. Ele tem a sintaxe a ?? b
, onde a
é um tipo opcional e b
é do mesmo tipo que a
(embora geralmente não opcional).
Essencialmente, permite que você diga “Se a
contém um valor, desembrulhe-o. Se não, volte em seu b
lugar ”. Por exemplo, você pode usá-lo assim:
let number = anOptionalInt ?? 0
Isso definirá uma number
constante do Int
tipo, que conterá o valor de anOptionalInt
, se contiver um valor ou 0
não.
É apenas uma abreviação de:
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 adicionando o sufixo ao nome da variável com um ?
ao usá-lo.
Por exemplo, digamos que temos uma variável foo
, do tipo uma Foo
instância opcional .
var foo : Foo?
Se quisermos chamar um método foo
que não retorna nada, podemos simplesmente fazer:
foo?.doSomethingInteresting()
Se foo
contiver um valor, este método será chamado nele. Do contrário, nada de ruim acontecerá - o código simplesmente continuará em execução.
(Este é um comportamento semelhante ao envio de mensagens nil
em Objective-C)
Portanto, isso também pode ser usado para definir propriedades, bem como métodos de chamada. Por exemplo:
foo?.bar = Bar()
Novamente, nada de ruim acontecerá aqui se foo
for nil
. Seu código simplesmente continuará em execução.
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á em Void?
vez de Void
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 foo
ser 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 métodos acima - ou desembrulhar a foo
si mesmo antes de acessar métodos ou chamar métodos que retornam valores.
Além disso, como o nome sugere, você pode "encadear" essas declarações. Isso significa que se foo
tiver uma propriedade opcional baz
, que possui uma propriedade qux
- você pode 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 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 variáveis opcionais. Se um opcional tiver um valor, você pode 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?
Ao aplicar a map
função a ele, podemos usar a stringByAppendingString
função para concatená-la com outra string.
Como stringByAppendingString
leva um argumento de string não opcional, não podemos inserir nossa string opcional diretamente. No entanto, ao usar map
, podemos usar permitir 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")
Porém, se anOptionalString
não tiver um valor, 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 map
, exceto que permite retornar outro opcional de dentro do corpo da tampa. Isso significa que você pode inserir um opcional em um processo que requer uma entrada não opcional, mas pode gerar um 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á detectado com segurança no catch
bloco.
A error
constante que você vê no catch
bloco não foi declarada por nós - ela é gerada automaticamente por catch
.
Você também pode error
se 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 try
essa maneira é a maneira correta de tentar, capturar e lidar com erros provenientes de funções de lançamento.
Também há 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 do 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 irá travar.
Você só deve usar try!
se puder provar que seu resultado nunca falhará em 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
Resposta TL; DR
Com muito poucas exceções , esta regra é de ouro:
Evite o uso de !
Declare a variável opcional ( ?
), não opcionais implicitamente desembrulhados (IUO) ( !
)
Em outras palavras, prefira usar:
var nameOfDaughter: String?
Ao invés de:
var nameOfDaughter: String!
Desembrulhe variável opcional usando if let
ouguard let
Desembrulhe a 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 pretendia ser concisa, para compreensão total leia a resposta aceita
Recursos
Esta pergunta surge O TEMPO TODO 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 podem conter um valor ou não. Em outras linguagens como C, você pode armazenar um valor de 0 em uma variável para indicar que ela 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.
Opcionais Swift permitem que você configure 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 para significar (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 "desdobramento forçado". 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á nulo." Se você estiver errado, você falha.
A menos que você realmente não sabe o que está fazendo, evitar o "!" forçar operador de desembrulhar. É provavelmente a maior fonte de travamentos para programadores Swift iniciantes.
Como lidar com opcionais:
Existem muitas outras maneiras de lidar com opcionais que são mais seguras. Aqui estão alguns (não uma lista exaustiva)
Você pode usar "ligação opcional" ou "if let" para dizer "se este opcional contém 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 ligação opcional com 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 ao usar biding opcional só existe (está apenas "no escopo") no corpo da instrução if.
Como alternativa, você pode usar uma instrução guard, que permite sair da 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 Guard foram adicionadas no Swift 2. Guard permite que você preserve o "caminho de ouro" através do seu código e evite níveis cada vez maiores de ifs aninhados que às vezes resultam do uso de vinculação opcional "if let".
Existe também uma construção chamada "operador de coalescência nula". Ele assume a forma "opcional_var ?? substituição_val". Ele retorna uma variável não opcional com o mesmo tipo dos 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 um código como este:
let newFoo = foo ?? "nil" // "??" is the nil coalescing operator
print("foo = \(newFoo)")
Você também pode usar o tratamento de erros try / catch ou guard, mas geralmente uma das outras técnicas acima é mais limpa.
EDITAR:
Outra pegadinha um pouco mais sutil com opcionais é "opcionais implicitamente desembrulhados. Quando declaramos foo, poderíamos dizer:
var foo: String!
Nesse caso, foo ainda é opcional, mas você não precisa desembrulhar para fazer referência a ele. Isso significa que sempre que você tentar fazer referência a foo, você travará se for nulo.
Portanto, este código:
var foo: String!
let upperFoo = foo.capitalizedString
Irá travar na referência à propriedade capitalizedString de foo, embora não estejamos forçando o desempacotamento de foo. a impressão parece boa, mas não é.
Portanto, você deve ter muito cuidado com opcionais desembrulhados implicitamente. (e talvez até mesmo evitá-los completamente até que você tenha um conhecimento sólido dos opcionais.)
Conclusão: quando você estiver aprendendo Swift pela primeira vez, finja que o "!" personagem não faz parte do idioma. É provável que você tenha problemas.
Uma vez que as respostas acima explicam claramente como jogar com segurança com opcionais. Vou tentar explicar o que os opcionais são realmente rápidos.
Outra maneira de declarar uma variável opcional é
var i : Optional<Int>
E o tipo opcional nada mais é do que uma enumeração com dois casos, ou seja,
enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
.
.
.
}
Portanto, para atribuir um 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)
De acordo com a Swift, 'nil' é 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 se Optionals
conformar ExpressibleByNilLiteral
e estar em conformidade com outros tipos é desencorajado.
ExpressibleByNilLiteral
tem um único método chamado init(nilLiteral:)
que inicializa uma instância com nil. Normalmente, você não chamará esse método e, de acordo com a documentação do Swift, é desencorajado chamar esse inicializador diretamente, pois o compilador o chama sempre que você inicializa um tipo opcional com nil
literal.
Até eu mesmo tenho que envolver (sem trocadilhos) minha cabeça em Optionals: D Happy Swfting All .
Primeiro, você deve saber o que é um valor opcional. Você pode acessar a linguagem de programação Swift para obter detalhes.
Em segundo lugar, você deve saber que o valor opcional tem dois status. Um é o valor total e o outro é um valor nulo. Portanto, antes de implementar um valor opcional, você deve verificar em qual estado ele está.
Você pode usar if let ...
ou guard let ... else
e assim por diante.
De outra forma, se você não quiser verificar o estado da variável antes de sua implementação, você também pode usar var buildingName = buildingName ?? "buildingName"
.
Tive este erro uma vez quando estava tentando definir meus valores de Outlets a partir do método de preparação para 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
}
}
}
Então descobri que não consigo 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 aí com o mesmo problema, pois descobri que a resposta marcada é um ótimo recurso para entender os opcionais e como eles funcionam, mas não abordei o problema diretamente.
Basicamente, você tentou usar um valor nulo em locais onde o Swift permite apenas valores não nulos, dizendo ao compilador para confiar em você que nunca haverá valor nulo lá, permitindo assim que seu aplicativo seja compilado.
Existem vários cenários que levam a este tipo de erro fatal:
desembrulhamento forçado:
let user = someVariable!
Se
someVariable
for nulo, você terá um crash. Ao fazer um desempacotamento forçado, você transferiu a responsabilidade de verificação nula do compilador para você; basicamente, ao fazer um desempacotamento forçado, você está garantindo ao compilador que nunca terá valores nulos ali. E adivinhe o que acontece se, de alguma forma, um valor nulo terminar emsomeVariable
?Solução? Use vinculação opcional (também conhecida como if-let), faça o processamento da variável lá:
if user = someVariable { // do your stuff }
conversões forçadas (para baixo):
let myRectangle = someShape as! Rectangle
Aqui, ao forçar a conversão, você diz ao compilador para não se preocupar mais, pois você sempre terá uma
Rectangle
instância lá. E enquanto isso durar, 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 vinculação opcional (também conhecida como if-let), faça o processamento da variável lá:
if let myRectangle = someShape as? Rectangle { // yay, I have a rectangle }
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 bagunçar a
name
propriedade configurando-a comonil
, ela funcionará conforme o esperado; no entanto, seUser
for inicializado de um JSON que não possui aname
chave, você obterá 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 diferente de zero 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 fará com que o compilador o ajude, informando os caminhos de código que você esqueceu ao atribuir 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 pelo XIB que deseja usar.
class SignInViewController: UIViewController { @IBOutlet var emailTextField: UITextField! }
Agora, se você esqueceu de conectar a tomada do editor XIB, então o aplicativo irá travar assim que você quiser usar a tomada. Solução? Certifique-se de que todas as tomadas estejam conectadas. Ou use o
?
operador sobre eles:emailTextField?.text = "[email protected]"
. Ou declare a saída como opcional, embora, neste caso, o compilador force você a desembrulhá-la em todo o código.Valores vindos 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
), aname
propriedade será importada em Swift comoString!
(um IUO - opcional desdobrado implicitamente). Assim que algum código rápido quiser usar o valor, ele irá travar sename
for nulo.Solução? Adicione anotações de nulidade ao seu código Objective-C. Porém, cuidado, o compilador Objective-C é um pouco permissivo quando se trata de nulidade, você pode acabar com valores nulos, mesmo se você os marcou explicitamente como
nonnull
.
Este é mais um comentário 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: Compila 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 desempacotar 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
}
Resumindo, var address : Address!
você está escondendo a possibilidade de uma variável ser nil
de outros leitores. E quando ele trava, você fica tipo "que diabos ?! meu address
não é opcional, então por que estou travando?!.
Portanto, é melhor escrever assim:
c1.address.city = c2.address!.city // ERROR: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Você pode agora me dizer que objeto era esse nil
?
Desta vez, o código ficou mais claro para você. Você pode racionalizar e pensar que provavelmente é o address
parâmetro que foi desembrulhado à força.
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
}
Os erros EXC_BAD_INSTRUCTION
e fatal 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 funcionam os Opcionais , mencionado em outras respostas, mas esta é a única vez que me aparece principalmente.
Se você receber este erro no CollectionView, tente criar o arquivo CustomCell e também o xib personalizado.
adicione este código em ViewDidLoad () em mainVC.
let nib = UINib(nibName: "CustomnibName", bundle: nil)
self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")
Encontrei este erro ao fazer uma transição de um controlador de exibição de tabela para um controlador de exibição porque tinha esquecido de especificar o nome da classe personalizada para o controlador de exibição no storyboard principal.
Algo simples que vale a pena verificar se tudo o mais parece bom