我的Swift程序崩溃与EXC_BAD_INSTRUCTION和以下类似错误之一。这个错误是什么意思,我该如何修复它?

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

or

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


这篇文章旨在收集“意外发现为零”问题的答案,这样它们就不会分散而难以找到。请随意添加您自己的答案或编辑现有的wiki答案。


当前回答

背景:什么是可选选项?

在Swift中,Optional<Wrapped>是一个选项类型:它可以包含原始类型("Wrapped")的任何值,或者根本没有值(特殊值nil)。可选值在使用之前必须展开。

Optional是一个泛型类型,这意味着Optional<Int>和Optional<String>是不同的类型——<>里面的类型称为Wrapped类型。实际上,Optional是一个有两种情况的enum: .some(Wrapped)和.none,其中.none等价于nil。

可选选项可以使用命名类型Optional<T>来声明,或者(最常见的)使用?后缀。

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

可选选项是一种简单而强大的工具,可以在编写代码时表达您的假设。编译器可以使用这些信息来防止你犯错误。摘自Swift编程语言:

Swift是一种类型安全的语言,这意味着该语言可以帮助你清楚你的代码可以使用的值的类型。如果你的部分代码需要一个String,类型安全可以防止你错误地传递一个Int。同样,类型安全可以防止您意外地将可选String传递给需要非可选String的代码段。类型安全可以帮助您在开发过程中尽早捕获并修复错误。

其他一些编程语言也有通用的选项类型:例如,在Haskell中是Maybe,在Rust中是option,在c++ 17中是optional。

In programming languages without option types, a particular "sentinel" value is often used to indicate the absence of a valid value. In Objective-C, for example, nil (the null pointer) represents the lack of an object. For primitive types such as int, a null pointer can't be used, so you would need either a separate variable (such as value: Int and isValid: Bool) or a designated sentinel value (such as -1 or INT_MIN). These approaches are error-prone because it's easy to forget to check isValid or to check for the sentinel value. Also, if a particular value is chosen as the sentinel, that means it can no longer be treated as a valid value.

选项类型,如Swift的Optional,通过引入一个特殊的、单独的nil值(这样你就不必指定一个哨兵值)来解决这些问题,并利用强类型系统,这样编译器就可以帮助你在必要时检查nil。


为什么我得到“致命错误:在打开可选值时意外地发现nil”?

为了访问一个可选项的值(如果它有的话),您需要将其展开。可选值可以安全或强制解包装。如果强制解包一个可选对象,而该对象没有值,则程序会因为上面的消息而崩溃。

Xcode将通过突出显示一行代码来显示崩溃。问题出在这一行上。

这种崩溃可以发生在两种不同的强制展开中:

1. 显式力展开

这是用!操作符是可选的。例如:

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

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

因为anOptionalString在这里是nil,你会在强制打开它的行上崩溃。

2. 隐式打开可选选项

它们是用!而不是?在类型后面。

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

假定这些可选项包含一个值。因此,每当您访问隐式解包装的可选选项时,它将自动强制为您解包装。如果它不包含值,它将崩溃。

print(optionalDouble) // <- CRASH

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

为了找出导致崩溃的变量,您可以按住“”键,同时单击以显示定义,在这里您可能会找到可选类型。

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.


什么时候我应该强制打开一个可选选项?

显式力展开

作为一般规则,永远不要使用!命令显式强制打开可选对象。操作符。可能在某些情况下使用!是可以接受的-但是只有当您100%确定可选选项包含一个值时才应该使用它。

虽然可能在某些情况下您可以使用强制展开,因为您知道一个可选选项包含一个值—没有一个地方您不能安全地展开该可选选项。

隐式打开可选选项

这些变量的设计使您可以将它们的赋值推迟到代码的后面部分。在访问它们之前,您有责任确保它们具有价值。然而,因为它们涉及强制展开,所以它们本质上仍然是不安全的——因为它们假设您的值是非nil,即使赋值为nil是有效的。

您应该只在最后的时候使用隐式打开的可选项。如果您可以使用惰性变量,或者为变量提供默认值,那么您应该这样做,而不是使用隐式打开的可选变量。

然而,在一些情况下,隐式展开可选选项是有益的,并且您仍然可以使用下面列出的各种安全展开它们的方法—但是您应该始终谨慎使用它们。


我怎样才能安全地处理选修课?

检查可选对象是否包含值的最简单方法是将其与nil进行比较。

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

然而,在使用可选项时,99.9%的情况下,您实际上希望访问它包含的值,如果它包含一个值的话。为此,您可以使用可选绑定。

可选的绑定

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

例如:

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

这样做的目的是首先检查可选项是否包含值。如果是这样,那么' unwrapped '值被分配给一个新变量(number) -然后你可以自由地使用它,就像它是非可选的一样。如果optional不包含值,那么else子句将被调用,正如您所期望的那样。

可选绑定的巧妙之处在于,你可以同时展开多个可选。你可以用逗号把两句话分开。如果所有可选选项都被打开,则语句将成功。

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

另一个巧妙的技巧是,您还可以使用逗号来检查值上的某个条件,在展开它之后。

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

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

守卫语句允许您定义成功的条件——只有满足该条件,当前作用域才会继续执行。它们用语法保护条件else{…}定义。

所以,要使用可选的绑定,你可以这样做:

guard let number = anOptionalInt else {
    return
}

(注意,在保护体中,必须使用其中一个控制转移语句才能退出当前执行代码的作用域)。

如果一个optionalint包含一个值,它将被解包装并分配给新的数字常量。警卫之后的代码将继续执行。如果它不包含值-守卫将执行括号内的代码,这将导致控制权的转移,因此紧随其后的代码将不会被执行。

关于guard语句的真正巧妙之处在于,现在可以在语句后面的代码中使用unwrapped值(我们知道,只有当optional有值时,未来的代码才能执行)。这对于消除嵌套多个if语句所产生的“末日金字塔”非常有用。

例如:

guard let number = anOptionalInt else {
    return
}

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

守卫还支持if语句所支持的简洁技巧,例如同时展开多个可选选项并使用where子句。

是否使用if或guard语句完全取决于将来的代码是否要求可选参数包含值。

无合并运算符

Nil Coalescing Operator是三元条件操作符的一个漂亮的简写版本,主要用于将可选项转换为非可选项。它有语法a ??B,其中a是可选类型,B是与a相同的类型(尽管通常是非可选的)。

它本质上允许您说“如果a包含一个值,则展开它”。如果没有,则返回b”。例如,你可以这样使用它:

let number = anOptionalInt ?? 0

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

它只是缩写:

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

可选的链接

可以使用可选链接来调用方法或访问可选对象上的属性。这可以通过在变量名后面加上?使用时。

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

var foo : Foo?

如果我们想在foo上调用一个不返回任何东西的方法,我们可以简单地这样做:

foo?.doSomethingInteresting()

如果foo包含一个值,这个方法将被调用。如果没有,也不会发生什么糟糕的事情——代码将继续执行。

(这类似于在Objective-C中向nil发送消息)

因此,这也可以用于设置属性以及调用方法。例如:

foo?.bar = Bar()

同样,如果foo为nil,这里也不会发生什么不好的事情。您的代码将继续执行。

可选链接允许您做的另一个巧妙的技巧是检查设置属性或调用方法是否成功。你可以通过将返回值与nil进行比较来做到这一点。

(这是因为可选值将返回Void?而不是Void方法不返回任何东西)

例如:

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

然而,当试图访问属性或调用返回值的方法时,事情变得有点棘手。因为foo是可选的,从它返回的任何东西也是可选的。为了解决这个问题,你可以在访问方法或调用返回值的方法之前,打开使用上述方法之一返回的可选选项,或者打开foo本身。

同样,顾名思义,你可以将这些语句“链接”在一起。这意味着如果foo有一个可选属性baz,它有一个属性qux -你可以这样写:

let optionalQux = foo?.baz?.qux

同样,因为foo和baz是可选的,所以无论qux本身是否是可选的,qux返回的值总是可选的。

map和flatMap

一个经常未充分使用的可选特性是使用map和flatMap函数的能力。这允许您将非可选转换应用到可选变量。如果可选对象具有值,则可以对其应用给定的转换。如果它没有值,它将保持为nil。

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

let anOptionalString:String?

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

因为stringByAppendingString接受一个非可选字符串参数,所以不能直接输入可选字符串。然而,通过使用map,如果anOptionalString有值,我们可以使用allow stringByAppendingString。

例如:

var anOptionalString:String? = "bar"

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

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

然而,如果anOptionalString没有值,map将返回nil。例如:

var anOptionalString:String?

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

print(anOptionalString) // nil

flatMap的工作原理与map类似,除了它允许您从闭包体中返回另一个可选参数。这意味着您可以在需要非可选输入的流程中输入一个可选输入,但可以输出一个可选输入本身。

try!

Swift的错误处理系统可以安全地使用Do-Try-Catch:

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?它吸收了误差:

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

但是Swift的错误处理系统也提供了一种“强制尝试”try!:

let result = try! someThrowingFunc()

这篇文章中解释的概念也适用于这里:如果抛出错误,应用程序将崩溃。

你应该只使用try!如果你能证明它的结果在你的环境中永远不会失败——这是非常罕见的。

大多数情况下,你会使用完整的“尝试-捕捉”系统——还有一个可选的,试试?,在极少数情况下,处理错误并不重要。


资源

关于Swift optional的苹果文档 何时使用,何时不使用隐式展开的可选选项 学习如何调试iOS应用程序崩溃

其他回答

我在从表视图控制器到视图控制器进行segue时遇到了这个错误,因为我忘记在主故事板中为视图控制器指定自定义类名。

一些简单的东西值得检查,如果其他看起来都没问题

简单地说 您正在尝试使用可选变量的值为nil。 快速修复可以使用保护或如果让而不是强制打开像放!在变量的末尾

如果我的情况下,我设置一个变量UILabel为nil。

所以我修复了它,之后它就没有抛出错误了。

代码片段

class ResultViewController: UIViewController {

    @IBOutlet weak var resultLabel: UILabel!
    var bmiValue=""
    override func viewDidLoad() {
        super.viewDidLoad()
        print(bmiValue)
        resultLabel.text=bmiValue //where bmiValue was nil , I fixed it and problem was solved

    }
    
    @IBAction func recaculateBmi(_ sender: UIButton) {
        self.dismiss(animated: true, completion: nil)
    }
    

}

背景:什么是可选选项?

在Swift中,Optional<Wrapped>是一个选项类型:它可以包含原始类型("Wrapped")的任何值,或者根本没有值(特殊值nil)。可选值在使用之前必须展开。

Optional是一个泛型类型,这意味着Optional<Int>和Optional<String>是不同的类型——<>里面的类型称为Wrapped类型。实际上,Optional是一个有两种情况的enum: .some(Wrapped)和.none,其中.none等价于nil。

可选选项可以使用命名类型Optional<T>来声明,或者(最常见的)使用?后缀。

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

可选选项是一种简单而强大的工具,可以在编写代码时表达您的假设。编译器可以使用这些信息来防止你犯错误。摘自Swift编程语言:

Swift是一种类型安全的语言,这意味着该语言可以帮助你清楚你的代码可以使用的值的类型。如果你的部分代码需要一个String,类型安全可以防止你错误地传递一个Int。同样,类型安全可以防止您意外地将可选String传递给需要非可选String的代码段。类型安全可以帮助您在开发过程中尽早捕获并修复错误。

其他一些编程语言也有通用的选项类型:例如,在Haskell中是Maybe,在Rust中是option,在c++ 17中是optional。

In programming languages without option types, a particular "sentinel" value is often used to indicate the absence of a valid value. In Objective-C, for example, nil (the null pointer) represents the lack of an object. For primitive types such as int, a null pointer can't be used, so you would need either a separate variable (such as value: Int and isValid: Bool) or a designated sentinel value (such as -1 or INT_MIN). These approaches are error-prone because it's easy to forget to check isValid or to check for the sentinel value. Also, if a particular value is chosen as the sentinel, that means it can no longer be treated as a valid value.

选项类型,如Swift的Optional,通过引入一个特殊的、单独的nil值(这样你就不必指定一个哨兵值)来解决这些问题,并利用强类型系统,这样编译器就可以帮助你在必要时检查nil。


为什么我得到“致命错误:在打开可选值时意外地发现nil”?

为了访问一个可选项的值(如果它有的话),您需要将其展开。可选值可以安全或强制解包装。如果强制解包一个可选对象,而该对象没有值,则程序会因为上面的消息而崩溃。

Xcode将通过突出显示一行代码来显示崩溃。问题出在这一行上。

这种崩溃可以发生在两种不同的强制展开中:

1. 显式力展开

这是用!操作符是可选的。例如:

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

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

因为anOptionalString在这里是nil,你会在强制打开它的行上崩溃。

2. 隐式打开可选选项

它们是用!而不是?在类型后面。

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

假定这些可选项包含一个值。因此,每当您访问隐式解包装的可选选项时,它将自动强制为您解包装。如果它不包含值,它将崩溃。

print(optionalDouble) // <- CRASH

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

为了找出导致崩溃的变量,您可以按住“”键,同时单击以显示定义,在这里您可能会找到可选类型。

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.


什么时候我应该强制打开一个可选选项?

显式力展开

作为一般规则,永远不要使用!命令显式强制打开可选对象。操作符。可能在某些情况下使用!是可以接受的-但是只有当您100%确定可选选项包含一个值时才应该使用它。

虽然可能在某些情况下您可以使用强制展开,因为您知道一个可选选项包含一个值—没有一个地方您不能安全地展开该可选选项。

隐式打开可选选项

这些变量的设计使您可以将它们的赋值推迟到代码的后面部分。在访问它们之前,您有责任确保它们具有价值。然而,因为它们涉及强制展开,所以它们本质上仍然是不安全的——因为它们假设您的值是非nil,即使赋值为nil是有效的。

您应该只在最后的时候使用隐式打开的可选项。如果您可以使用惰性变量,或者为变量提供默认值,那么您应该这样做,而不是使用隐式打开的可选变量。

然而,在一些情况下,隐式展开可选选项是有益的,并且您仍然可以使用下面列出的各种安全展开它们的方法—但是您应该始终谨慎使用它们。


我怎样才能安全地处理选修课?

检查可选对象是否包含值的最简单方法是将其与nil进行比较。

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

然而,在使用可选项时,99.9%的情况下,您实际上希望访问它包含的值,如果它包含一个值的话。为此,您可以使用可选绑定。

可选的绑定

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

例如:

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

这样做的目的是首先检查可选项是否包含值。如果是这样,那么' unwrapped '值被分配给一个新变量(number) -然后你可以自由地使用它,就像它是非可选的一样。如果optional不包含值,那么else子句将被调用,正如您所期望的那样。

可选绑定的巧妙之处在于,你可以同时展开多个可选。你可以用逗号把两句话分开。如果所有可选选项都被打开,则语句将成功。

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

另一个巧妙的技巧是,您还可以使用逗号来检查值上的某个条件,在展开它之后。

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

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

守卫语句允许您定义成功的条件——只有满足该条件,当前作用域才会继续执行。它们用语法保护条件else{…}定义。

所以,要使用可选的绑定,你可以这样做:

guard let number = anOptionalInt else {
    return
}

(注意,在保护体中,必须使用其中一个控制转移语句才能退出当前执行代码的作用域)。

如果一个optionalint包含一个值,它将被解包装并分配给新的数字常量。警卫之后的代码将继续执行。如果它不包含值-守卫将执行括号内的代码,这将导致控制权的转移,因此紧随其后的代码将不会被执行。

关于guard语句的真正巧妙之处在于,现在可以在语句后面的代码中使用unwrapped值(我们知道,只有当optional有值时,未来的代码才能执行)。这对于消除嵌套多个if语句所产生的“末日金字塔”非常有用。

例如:

guard let number = anOptionalInt else {
    return
}

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

守卫还支持if语句所支持的简洁技巧,例如同时展开多个可选选项并使用where子句。

是否使用if或guard语句完全取决于将来的代码是否要求可选参数包含值。

无合并运算符

Nil Coalescing Operator是三元条件操作符的一个漂亮的简写版本,主要用于将可选项转换为非可选项。它有语法a ??B,其中a是可选类型,B是与a相同的类型(尽管通常是非可选的)。

它本质上允许您说“如果a包含一个值,则展开它”。如果没有,则返回b”。例如,你可以这样使用它:

let number = anOptionalInt ?? 0

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

它只是缩写:

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

可选的链接

可以使用可选链接来调用方法或访问可选对象上的属性。这可以通过在变量名后面加上?使用时。

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

var foo : Foo?

如果我们想在foo上调用一个不返回任何东西的方法,我们可以简单地这样做:

foo?.doSomethingInteresting()

如果foo包含一个值,这个方法将被调用。如果没有,也不会发生什么糟糕的事情——代码将继续执行。

(这类似于在Objective-C中向nil发送消息)

因此,这也可以用于设置属性以及调用方法。例如:

foo?.bar = Bar()

同样,如果foo为nil,这里也不会发生什么不好的事情。您的代码将继续执行。

可选链接允许您做的另一个巧妙的技巧是检查设置属性或调用方法是否成功。你可以通过将返回值与nil进行比较来做到这一点。

(这是因为可选值将返回Void?而不是Void方法不返回任何东西)

例如:

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

然而,当试图访问属性或调用返回值的方法时,事情变得有点棘手。因为foo是可选的,从它返回的任何东西也是可选的。为了解决这个问题,你可以在访问方法或调用返回值的方法之前,打开使用上述方法之一返回的可选选项,或者打开foo本身。

同样,顾名思义,你可以将这些语句“链接”在一起。这意味着如果foo有一个可选属性baz,它有一个属性qux -你可以这样写:

let optionalQux = foo?.baz?.qux

同样,因为foo和baz是可选的,所以无论qux本身是否是可选的,qux返回的值总是可选的。

map和flatMap

一个经常未充分使用的可选特性是使用map和flatMap函数的能力。这允许您将非可选转换应用到可选变量。如果可选对象具有值,则可以对其应用给定的转换。如果它没有值,它将保持为nil。

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

let anOptionalString:String?

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

因为stringByAppendingString接受一个非可选字符串参数,所以不能直接输入可选字符串。然而,通过使用map,如果anOptionalString有值,我们可以使用allow stringByAppendingString。

例如:

var anOptionalString:String? = "bar"

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

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

然而,如果anOptionalString没有值,map将返回nil。例如:

var anOptionalString:String?

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

print(anOptionalString) // nil

flatMap的工作原理与map类似,除了它允许您从闭包体中返回另一个可选参数。这意味着您可以在需要非可选输入的流程中输入一个可选输入,但可以输出一个可选输入本身。

try!

Swift的错误处理系统可以安全地使用Do-Try-Catch:

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?它吸收了误差:

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

但是Swift的错误处理系统也提供了一种“强制尝试”try!:

let result = try! someThrowingFunc()

这篇文章中解释的概念也适用于这里:如果抛出错误,应用程序将崩溃。

你应该只使用try!如果你能证明它的结果在你的环境中永远不会失败——这是非常罕见的。

大多数情况下,你会使用完整的“尝试-捕捉”系统——还有一个可选的,试试?,在极少数情况下,处理错误并不重要。


资源

关于Swift optional的苹果文档 何时使用,何时不使用隐式展开的可选选项 学习如何调试iOS应用程序崩溃

这个问题在《SO》杂志上经常出现。这是Swift新开发人员首先要解决的问题之一。

背景:

Swift使用“可选项”的概念来处理可能包含值或不包含值的值。在C等其他语言中,可以在变量中存储0值,以表示它不包含任何值。但是,如果0是一个有效值呢?然后你可以用-1。如果-1是一个有效值怎么办?等等。

Swift可选选项允许您设置任何类型的变量,以包含有效值或无值。

当将变量声明为mean(类型x,或无值)时,在类型后加上一个问号。

可选对象实际上是一个容器,它要么包含给定类型的变量,要么什么都不包含。

为了获取其中的值,一个可选对象需要“解包装”。

“!”操作符是一个“强制展开”操作符。上面写着“相信我。我知道我在做什么。我保证当这段代码运行时,变量将不包含nil。”如果你错了,你就会崩溃。

除非您确实知道自己在做什么,否则请避免使用“!”强制展开操作符。对于初学者来说,它可能是最大的崩溃来源。

如何处理可选选项:

还有很多其他更安全的方法来处理可选选项。以下是一些(不是详尽的列表)

您可以使用“可选绑定”或“if let”来表示“如果这个可选包含一个值,则将该值保存到一个新的非可选变量中。”如果optional不包含值,则跳过该If语句的语句体”。

下面是一个使用foo optional进行可选绑定的例子:

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

注意,使用可选投标时定义的变量只存在于if语句体中(仅“在作用域内”)。

或者,你可以使用一个guard语句,它允许你在变量为nil时退出你的函数:

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

在Swift 2中增加了警卫声明。Guard允许您在代码中保留“黄金路径”,并避免由于使用“if let”可选绑定而导致的不断增加的嵌套if级别。

还有一种构造叫做“nil合并运算符”。它的形式是“optional_var ??”replacement_val”。它返回一个与可选变量中包含的数据类型相同的非可选变量。如果可选对象包含nil,它将返回"??"符号后面的表达式值。

所以你可以使用这样的代码:

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

您也可以使用try/catch或guard错误处理,但通常上述另一种技术更简洁。

编辑:

另一个更微妙的问题是“隐式地展开可选项”。当我们声明foo时,我们可以说:

var foo: String!

在这种情况下,foo仍然是可选的,但你不必打开它来引用它。这意味着任何时候你试图引用foo,如果它是nil,就会崩溃。

这段代码:

var foo: String!


let upperFoo = foo.capitalizedString

将在引用foo的capitalizedString属性时崩溃,即使我们没有强制展开foo。指纹看起来很好,但实际上不是。

因此,您要非常小心地使用隐式展开的可选项。(甚至可能完全避免它们,直到你对可选选项有了充分的理解。)

底线:当你第一次学习Swift时,假装“!”字符不是语言的一部分。这很可能会给你带来麻烦。