“致命错误:在展开可选值时意外发现nil”是什么意思?意思? [英] What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?

查看:827
本文介绍了“致命错误:在展开可选值时意外发现nil”是什么意思?意思?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的Swift程序因 EXC_BAD_INSTRUCTION 和以下类似错误之一而崩溃。该错误是什么意思,以及我该如何解决?


致命错误:在展开可选值时意外发现nil



致命错误:意外发现nil,同时隐式展开一个可选值







旨在收集意外发现零问题的答案,以使它们不会分散且很难找到。随时添加您自己的答案或,或提供变量的默认值 –您应该这样做,而不是使用隐式解包的可选变量。



但是,在很少有方案中隐式解开的可选方案很有用,您仍然可以使用各种如下所示安全地解开它们的方法-但您应始终谨慎使用它们。






< h2>如何安全地处理Optionals?

检查Option是否包含值的最简单方法是比较如果它是 nil

 如果anOptionalInt!= nil {
print(包含值!)
}否则{
print(不包含值。)
}

但是,在使用可选项的99.9%的时间内,您实际上要访问它包含的值(如果它包含一个值)。为此,您可以使用可选绑定



可选绑定



可选绑定允许您检查可选变量是否包含值,并允许您将展开的值分配给新变量或常量。它使用语法 if let x = anOptional {...} if var x = anOptional {...} ,具体取决于绑定后是否需要修改新变量的值。



例如:

 如果让number = anOptionalInt {
print(包含一个值!它是\(number)!)
}否则{
print( 不包含数字)
}

这是先检查一下可选包含一个值。如果这样做,则将 unwrapped值分配给新变量( number )–然后您就可以像使用它那样自由使用它了。非可选。如果可选的包含一个值,则将按您期望的那样调用else子句。



可选绑定有什么精巧之处,是您可以同时解开多个可选项。您可以只用逗号分隔语句。

  var anOptionalInt:Int? 
var anOptionalString:字符串?

如果让数字= anOptionalInt,让文本= anOptionalString {
print( anOptionalInt包含一个值:\(数字)。anOptionalString也是如此,它是:\(文本) )
}否则{
print(一个或多个可选变量不包含值)
}

另一个巧妙的窍门是,在展开值后,您还可以使用逗号来检查值的特定条件。

 如果让数字= anOptionalInt,数字> 0 {
print( anOptionalInt包含一个值:\(number),并且它大于零!)
}

在if语句中使用可选绑定的唯一陷阱是,您只能从语句范围内访问未包装的值。如果需要从语句范围之外访问该值,则可以使用 guard语句



A 保护声明允许您定义成功条件–只有满足该条件,当前作用域才会继续执行。它们是用语法 guard condition else {...} 定义的。



因此,要将它们与可选绑定,您可以这样做:

 后卫让号= anOptionalInt其他{
return
}

(请注意,在护罩内,您必须使用一个控制转移语句,以退出当前正在执行的代码的范围。



如果 anOptionalInt 包含一个值,它将被解包并分配给新的 number 常量。然后,保护程序 之后的代码将继续执行。如果它不包含值,则守卫将执行方括号内的代码,这将导致控制权的转移,因此紧随其后的代码将不会被执行。



关于保护语句的真正整洁之处在于,现在可以在该语句后的代码中使用未包装的值(因为我们知道,将来的代码只能 执行,如果可选值具有值)。这对于消除通过嵌套多个if语句而创建的厄运金字塔 很有用。



例如:

 后卫让号= anOptionalInt else {
return
}

print( anOptionalInt包含一个值,它是:\(数字)!)

Guard还支持if语句支持的相同技巧,例如同时解开多个可选内容并使用 where 子句。



是否使用if或guard语句完全取决于将来的任何代码是否需要包含值。



零合并运算符



Nil合并运算符是该版本的精简版本三元条件运算符,主要用于将可选内容转换为非可选内容。它的语法为 a? b ,其中 a 是可选类型, b 与<$相同c $ c> a (尽管通常不是可选的。)



从本质上讲,您可以说如果 a 包含一个值,将其解包。如果没有,则返回 b 。例如,可以这样使用它:

  let number = anOptionalInt? 0 

这将定义数字常数 Int 类型,如果包含值,则将包含 anOptionalInt 的值,或 0 否则。



这只是简写:

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



可选链接



您可以使用可选链接,以便调用方法或访问可选对象上的属性。只需在使用时在变量名后加上即可。



例如,说我们有变量 foo ,类型为可选的 Foo 实例。

  var foo:F? 

如果我们想在 foo 不返回任何内容,我们可以简单地做到:

  foo?.doSomethingInteresting()

如果 foo 包含一个值,则将在此方法上调用。如果没有,则不会发生任何不好的事情-代码将继续执行。



(这类似于向 nil 在Objective-C中)



因此,它也可以用来设置属性和调用方法。例如:

  foo?.bar = Bar()

同样,如果 foo nil 。您的代码将继续执行。



可选链可以让您完成的另一个巧妙技巧是检查设置属性或调用方法是否成功。您可以通过将返回值与 nil 进行比较来实现此目的。



(这是因为可选值将在不返回任何内容的方法上返回 Void?而不是 Void



例如:

  if(foo?.bar = Bar()) != nil {
print( bar设置成功)
}否则{
print( bar设置成功)
}

但是,在尝试访问返回值的属性或调用方法时,事情变得有些棘手。因为 foo 是可选的,所以返回的任何内容也是可选的。为了解决这个问题,您可以解开使用上述方法之一返回的可选选项,也可以先解开 foo 本身,然后再访问返回值的方法或调用返回值的方法。



此外,顾名思义,您可以将这些语句链接在一起。这意味着,如果 foo 具有可选属性 baz ,其具有属性 qux –您可以编写以下内容:

  let optionalQux = foo?.baz?.qux 

再次,因为 foo baz 是可选的,从 qux 返回的值将始终是可选的,无论 qux 本身是可选的。



地图 flatMap



具有可选功能的经常使用不足的功能是能够使用 map flatMap 函数。这些允许您将非可选转换应用于可选变量。如果可选值具有值,则可以对其应用给定的转换。如果没有值,则将保留 nil



例如,假设您有一个可选字符串:

  let anOptionalString:String? 

通过对其应用 map 函数–我们可以使用 stringByAppendingString 函数将其连接到另一个字符串。



因为 stringByAppendingString 使用非可选的字符串参数,我们无法直接输入可选的字符串。但是,通过使用 map ,如果 anOptionalString stringByAppendingString 。 code>有一个值。



例如:

  var anOptionalString:String? = bar 

anOptionalString = anOptionalString.map {
中的unwrappedString返回 foo .stringByAppendingString(unwrappedString)
}

print(anOptionalString) // Optional( foobar)

但是,如果 anOptionalString 没有值, map 将返回 nil 。例如:

  var anOptionalString:String? 

anOptionalString = anOptionalString.map {
中的unwrappedString返回 foo .stringByAppendingString(unwrappedString)
}

print(anOptionalString)//无b $ b

flatMap 的工作方式与 map ,不同之处在于它允许您从闭包体内返回另一个可选。这意味着您可以将可选项输入到需要非可选输入的过程中,但可以输出可选项本身。



尝试! code>



Swift的错误处理系统可以与进行尝试捕捉

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

如果 someThrowingFunc()抛出错误,该错误将被安全地捕获在 catch 块中。



错误您在 catch 块中看到的常量尚未被我们声明-它是由 catch 自动生成的。



您也可以自己声明错误,它具有将其转换为有用格式的优点,例如:

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

使用 try 这种方法是尝试,捕获和处理来自抛出函数的错误的正确方法。



还有 try?会吸收错误:

 如果让结果=尝试? someThrowingFunc(){
//很酷的
}其他{
//处理失败,但没有错误信息
}

但是Swift的错误处理系统还提供了一种通过 try来强制尝试的方法!

 让结果=尝试! someThrowingFunc()

本文中解释的概念也适用于此:如果引发错误,则应用程序会崩溃。



如果只能证明其结果永远不会,则应该只使用 try!在您的环境中失败-这是非常罕见的。



在大多数情况下,您将使用完整的Do-Try-Catch系统-以及可选的系统,尝试?,在极少数情况下,处理错误并不重要。






资源




My Swift program is crashing with EXC_BAD_INSTRUCTION and one of the following similar errors. What does this error mean, and how do I fix it?

Fatal error: Unexpectedly found nil while unwrapping an Optional value

or

Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value


This post is intended to collect answers to "unexpectedly found nil" issues, so that they are not scattered and hard to find. Feel free to add your own answer or edit the existing wiki answer.

解决方案

This answer is community wiki. If you feel it could be made better, feel free to edit it!

Background: What’s an Optional?

In Swift, Optional is a generic type that can contain a value (of any kind), or no value at all.

In many other programming languages, a particular "sentinel" value is often used to indicate a lack of a value. In Objective-C, for example, nil (the null pointer) indicates the lack of an object. But this gets more tricky when working with primitive types — should -1 be used to indicate the absence of an integer, or perhaps INT_MIN, or some other integer? If any particular value is chosen to mean "no integer", that means it can no longer be treated as a valid value.

Swift is a type-safe language, which means the language helps you to be clear about the types of values your code can work with. If part of your code expects a String, type safety prevents you from passing it an Int by mistake.

In Swift, any type can be made optional. An optional value can take on any value from the original type, or the special value nil.

Optionals are defined with a ? suffix on the type:

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

The lack of a value in an optional is indicated by nil:

anOptionalInt = nil

(Note that this nil is not the same as the nil in Objective-C. In Objective-C, nil is the absence of a valid object pointer; in Swift, Optionals are not restricted to objects/reference types. Optional behaves similarly to Haskell's Maybe.)


Why did I get "fatal error: unexpectedly found nil while unwrapping an Optional value"?

In order to access an optional’s value (if it has one at all), you need to unwrap it. An optional value can be unwrapped safely or forcibly. If you force-unwrap an optional, and it didn't have a value, your program will crash with the above message.

Xcode will show you the crash by highlighting a line of code. The problem occurs on this line.

This crash can occur with two different kinds of force-unwrap:

1. Explicit Force Unwrapping

This is done with the ! operator on an optional. For example:

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

Fatal error: Unexpectedly found nil while unwrapping an Optional value

As anOptionalString is nil here, you will get a crash on the line where you force unwrap it.

2. Implicitly Unwrapped Optionals

These are defined with a !, rather than a ? after the type.

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

These optionals are assumed to contain a value. Therefore whenever you access an implicitly unwrapped optional, it will automatically be force unwrapped for you. If it doesn’t contain a value, it will crash.

print(optionalDouble) // <- CRASH

Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

In order to work out which variable caused the crash, you can hold while clicking to show the definition, where you might find the optional type.

IBOutlets, in particular, are usually implicitly unwrapped optionals. This is because your xib or storyboard will link up the outlets at runtime, after initialization. You should therefore ensure that you’re not accessing outlets before they're loaded in. You also should check that the connections are correct in your storyboard/xib file, otherwise the values will be nil at runtime, and therefore crash when they are implicitly unwrapped. When fixing connections, try deleting the lines of code that define your outlets, then reconnect them.


When should I ever force unwrap an Optional?

Explicit Force Unwrapping

As a general rule, you should never explicitly force unwrap an optional with the ! operator. There may be cases where using ! is acceptable – but you should only ever be using it if you are 100% sure that the optional contains a value.

While there may be an occasion where you can use force unwrapping, as you know for a fact that an optional contains a value – there is not a single place where you cannot safely unwrap that optional instead.

Implicitly Unwrapped Optionals

These variables are designed so that you can defer their assignment until later in your code. It is your responsibility to ensure they have a value before you access them. However, because they involve force unwrapping, they are still inherently unsafe – as they assume your value is non-nil, even though assigning nil is valid.

You should only be using implicitly unwrapped optionals as a last resort. If you can use a lazy variable, or provide a default value for a variable – you should do so instead of using an implicitly unwrapped optional.

However, there are a few scenarios where implicitly unwrapped optionals are beneficial, and you are still able to use various ways of safely unwrapping them as listed below – but you should always use them with due caution.


How can I safely deal with Optionals?

The simplest way to check whether an optional contains a value, is to compare it to nil.

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

However, 99.9% of the time when working with optionals, you’ll actually want to access the value it contains, if it contains one at all. To do this, you can use Optional Binding.

Optional Binding

Optional Binding allows you to check if an optional contains a value – and allows you to assign the unwrapped value to a new variable or constant. It uses the syntax if let x = anOptional {...} or if var x = anOptional {...}, depending if you need to modify the value of the new variable after binding it.

For example:

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

What this does is first check that the optional contains a value. If it does, then the ‘unwrapped’ value is assigned to a new variable (number) – which you can then freely use as if it were non-optional. If the optional doesn’t contain a value, then the else clause will be invoked, as you would expect.

What’s neat about optional binding, is you can unwrap multiple optionals at the same time. You can just separate the statements with a comma. The statement will succeed if all the optionals were unwrapped.

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

Another neat trick is that you can also use commas to check for a certain condition on the value, after unwrapping it.

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

The only catch with using optional binding within an if statement, is that you can only access the unwrapped value from within the scope of the statement. If you need access to the value from outside of the scope of the statement, you can use a guard statement.

A guard statement allows you to define a condition for success – and the current scope will only continue executing if that condition is met. They are defined with the syntax guard condition else {...}.

So, to use them with an optional binding, you can do this:

guard let number = anOptionalInt else {
    return
}

(Note that within the guard body, you must use one of the control transfer statements in order to exit the scope of the currently executing code).

If anOptionalInt contains a value, it will be unwrapped and assigned to the new number constant. The code after the guard will then continue executing. If it doesn’t contain a value – the guard will execute the code within the brackets, which will lead to transfer of control, so that the code immediately after will not be executed.

The real neat thing about guard statements is the unwrapped value is now available to use in code that follows the statement (as we know that future code can only execute if the optional has a value). This is a great for eliminating ‘pyramids of doom’ created by nesting multiple if statements.

For example:

guard let number = anOptionalInt else {
    return
}

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

Guards also support the same neat tricks that the if statement supported, such as unwrapping multiple optionals at the same time and using the where clause.

Whether you use an if or guard statement completely depends on whether any future code requires the optional to contain a value.

Nil Coalescing Operator

The Nil Coalescing Operator is a nifty shorthand version of the ternary conditional operator, primarily designed to convert optionals to non-optionals. It has the syntax a ?? b, where a is an optional type and b is the same type as a (although usually non-optional).

It essentially lets you say "If a contains a value, unwrap it. If it doesn’t then return b instead". For example, you could use it like this:

let number = anOptionalInt ?? 0

This will define a number constant of Int type, that will either contain the value of anOptionalInt, if it contains a value, or 0 otherwise.

It’s just shorthand for:

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

Optional Chaining

You can use Optional Chaining in order to call a method or access a property on an optional. This is simply done by suffixing the variable name with a ? when using it.

For example, say we have a variable foo, of type an optional Foo instance.

var foo : Foo?

If we wanted to call a method on foo that doesn’t return anything, we can simply do:

foo?.doSomethingInteresting()

If foo contains a value, this method will be called on it. If it doesn’t, nothing bad will happen – the code will simply continue executing.

(This is similar behaviour to sending messages to nil in Objective-C)

This can therefore also be used to set properties as well as call methods. For example:

foo?.bar = Bar()

Again, nothing bad will happen here if foo is nil. Your code will simply continue executing.

Another neat trick that optional chaining lets you do is check whether setting a property or calling a method was successful. You can do this by comparing the return value to nil.

(This is because an optional value will return Void? rather than Void on a method that doesn’t return anything)

For example:

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

However, things become a little bit more tricky when trying to access properties or call methods that return a value. Because foo is optional, anything returned from it will also be optional. To deal with this, you can either unwrap the optionals that get returned using one of the above methods – or unwrap foo itself before accessing methods or calling methods that return values.

Also, as the name suggests, you can ‘chain’ these statements together. This means that if foo has an optional property baz, which has a property qux – you could write the following:

let optionalQux = foo?.baz?.qux

Again, because foo and baz are optional, the value returned from qux will always be an optional regardless of whether qux itself is optional.

map and flatMap

An often underused feature with optionals is the ability to use the map and flatMap functions. These allow you to apply non-optional transforms to optional variables. If an optional has a value, you can apply a given transformation to it. If it doesn’t have a value, it will remain nil.

For example, let’s say you have an optional string:

let anOptionalString:String?

By applying the map function to it – we can use the stringByAppendingString function in order to concatenate it to another string.

Because stringByAppendingString takes a non-optional string argument, we cannot input our optional string directly. However, by using map, we can use allow stringByAppendingString to be used if anOptionalString has a value.

For example:

var anOptionalString:String? = "bar"

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

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

However, if anOptionalString doesn’t have a value, map will return nil. For example:

var anOptionalString:String?

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

print(anOptionalString) // nil

flatMap works similarly to map, except it allows you to return another optional from within the closure body. This means you can input an optional into a process that requires a non-optional input, but can output an optional itself.

try!

Swift's error handling system can be safely used with Do-Try-Catch:

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

If someThrowingFunc() throws an error, the error will be safely caught in the catch block.

The error constant you see in the catch block has not been declared by us - it's automatically generated by catch.

You can also declare error yourself, it has the advantage of being able to cast it to a useful format, for example:

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

Using try this way is the proper way to try, catch and handle errors coming from throwing functions.

There's also try? which absorbs the error:

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

But Swift's error handling system also provides a way to "force try" with try!:

let result = try! someThrowingFunc()

The concepts explained in this post also apply here: if an error is thrown, the application will crash.

You should only ever use try! if you can prove that its result will never fail in your context - and this is very rare.

Most of the time you will use the complete Do-Try-Catch system - and the optional one, try?, in the rare cases where handling the error is not important.


Resources

这篇关于“致命错误:在展开可选值时意外发现nil”是什么意思?意思?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆