“致命错误:解开可选值时意外发现 nil”是什么意思?

What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?

提问人: 提问时间:8/24/2015 最后编辑:13 revs, 8 users 43%pkamb 更新时间:10/27/2023 访问量:626871

问:

我的 Swift 程序崩溃,出现以下类似错误之一。这个错误是什么意思,我该如何解决?EXC_BAD_INSTRUCTION

致命错误:解开 Optional 值时意外发现 nil

致命错误:隐式解开 Optional 值时意外发现 nil


这篇文章旨在收集“意外发现无”问题的答案,以免它们分散且难以找到。您可以随意添加自己的答案或编辑现有的 wiki 答案。

Swift 异常 错误处理

评论

1赞 JonesJr876 7/21/2021
如果这与日期格式化器有关,请查看此 stackoverflow.com/questions/35002876/...
1赞 Qazi Ammar 1/2/2023
Unexpectedly found nil while unwrapping an Optional value错误就像每个 swift 程序员在它的首字母缩写日及之后收到的错误:-)祝您编码愉快!!greeting card

答:

771赞 35 revs, 17 users 55%Hamish #1

背景:什么是可选的?

在 Swift 中,Optional<Wrapped > 是一种选项类型:它可以包含原始 (“Wrapped”) 类型的任何值,也可以完全不包含任何值 (特殊值)。必须先解开可选值的包装,然后才能使用它。nil

Optional 是泛型类型,这意味着 和 是不同的类型,其中的类型称为 Wrapped 类型。在后台,Optional 是具有两种情况的枚举: 和 ,其中等价于 .Optional<Int>Optional<String><>.some(Wrapped).none.nonenil

可选选项可以使用命名类型声明,或者(最常见的)作为带有后缀的简写来声明。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 传递给需要非可选 String 的代码段。类型安全可帮助您在开发过程中尽早发现和修复错误。StringInt

其他一些编程语言也有通用的选项类型:例如,Haskell 中的 Maybe 和 Rust 中的 option,以及 C++17 中的可选

在没有选项类型的编程语言中,通常使用特定的“哨兵”值来指示缺少有效值。例如,在 Objective-C 中,(空指针)表示缺少对象。对于基元类型(如 ),不能使用 null 指针,因此需要单独的变量(如 和 )或指定的哨兵值(如 或 )。这些方法容易出错,因为很容易忘记检查或检查哨兵值。此外,如果选择特定值作为哨兵,则意味着它不能再被视为有效值。nilintvalue: IntisValid: Bool-1INT_MINisValid

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


为什么我收到“致命错误:解包可选值时意外发现 nil”?

为了访问可选的值(如果它有的话),你需要解开它。可选值可以安全或强制解包。如果强制解包一个可选值,并且它没有值,则程序将崩溃并显示上述消息。

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

crashed line

这种崩溃可能发生在两种不同类型的强制解包中:

1. 显式强制解包

这是通过可选的运算符完成的。例如:!

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

致命错误:解开 Optional 值时意外发现 nil

就像这里一样,你会在强制解开它的线上崩溃。anOptionalStringnil

2. 隐式解包的可选组件

它们在类型后用 a 而不是 a 来定义。!?

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

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

print(optionalDouble) // <- CRASH

致命错误:式解开 Optional 值时意外发现 nil

为了确定哪个变量导致了崩溃,您可以在单击时按住以显示定义,您可以在其中找到可选类型。

特别是 IBOutlets,通常是隐式解包的可选选项。这是因为您的 xib 或 storyboard 将在初始化在运行时链接插座。因此,您应该确保在加载插座之前不会访问插座。您还应该检查 storyboard/xib 文件中的连接是否正确,否则这些值将在运行时出现,因此在隐式解包时会崩溃。修复连接时,请尝试删除定义插座的代码行,然后重新连接它们。nil


我应该在什么时候强制解开可选包?

显式强制解包

作为一般规则,切勿使用运算符显式强制解开可选包。在某些情况下,使用是可以接受的 - 但只有在 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")
}

这样做的目的是首先检查可选值是否包含值。如果是这样,那么“解包”值将被分配给一个新变量 ()——然后您可以自由使用该变量,就好像它是非可选的一样。如果 optional 包含值,则将调用 else 子句,如您所料。number

可选绑定的巧妙之处在于,您可以同时解开多个可选绑定。您可以用逗号分隔语句。如果所有可选选项都已解包,则语句将成功。

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 语句

guard 语句允许您定义成功的条件 - 并且当前范围只有在满足该条件时才会继续执行。它们使用语法 定义。guard condition else {...}

因此,若要将它们与可选绑定一起使用,可以执行以下操作:

guard let number = anOptionalInt else {
    return
}

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

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

guard 语句的真正巧妙之处在于,解包的值现在可以在语句后面的代码中使用(因为我们知道,只有当可选值具有值时,未来的代码才能执行)。这对于消除由嵌套多个 if 语句创建的“厄运金字塔”非常有用。

例如:

guard let number = anOptionalInt else {
    return
}

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

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

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

Nil Coalescing 运算符

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

它本质上让你说“如果包含一个值,请解开它。如果没有,则返回”。例如,您可以像这样使用它:ab

let number = anOptionalInt ?? 0

这将定义一个类型的常量,如果它包含值,则该常量将包含值,否则将包含值。numberIntanOptionalInt0

它只是以下的简写:

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

可选链接

可以使用可选链接来调用方法或访问可选的属性。这只需在使用变量名称时在变量名称后加上 a 即可完成。?

例如,假设我们有一个变量,其类型为可选实例。fooFoo

var foo : Foo?

如果我们想调用一个不返回任何内容的方法,我们可以简单地执行:foo

foo?.doSomethingInteresting()

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

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

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

foo?.bar = Bar()

同样,如果是,这里不会发生任何不好的事情。您的代码将继续执行。foonil

可选链接允许您执行的另一个巧妙技巧是检查设置属性或调用方法是否成功。为此,可以将返回值与 进行比较。nil

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

例如:

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

但是,当尝试访问属性或调用返回值的方法时,事情会变得更加棘手。因为是可选的,所以从它返回的任何内容也将是可选的。要解决此问题,可以使用上述方法之一解包返回的可选选项,或者在访问方法或调用返回值的方法之前解包自身。foofoo

此外,顾名思义,您可以将这些语句“链接”在一起。这意味着,如果有一个可选属性,它有一个属性——你可以写如下:foobazqux

let optionalQux = foo?.baz?.qux

同样,由于 和 是可选的,因此无论其本身是否可选,从 返回的值都将始终是可选的。foobazquxqux

mapflatMap

一个经常未被充分利用的可选功能是能够使用 和 函数。这些允许您将非可选转换应用于可选变量。如果可选值具有值,则可以对其应用给定的转换。如果它没有值,它将保留 .mapflatMapnil

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

let anOptionalString:String?

通过将函数应用于它——我们可以使用该函数将其连接到另一个字符串。mapstringByAppendingString

因为采用非可选字符串参数,我们不能直接输入可选字符串。但是,通过使用 ,我们可以使用 allow to be used 如果具有值。stringByAppendingStringmapstringByAppendingStringanOptionalString

例如:

var anOptionalString:String? = "bar"

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

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

但是,如果没有值,将返回 .例如:anOptionalStringmapnil

var anOptionalString:String?

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

print(anOptionalString) // nil

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

try!

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

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

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

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

您也可以声明自己,它的优点是能够将其转换为有用的格式,例如:error

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!——这是非常罕见的。

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


资源

评论

116赞 Pranav Wadhwa 6/6/2016
就我个人而言,我要感谢你为写所有这些付出的努力。我觉得这肯定会对使用 swift 的初学者和专家有所帮助。
19赞 jtbandes 6/6/2016
我很高兴你发现它有帮助。这个答案是社区维基,所以这是许多人之间的合作(到目前为止有 7 人)!
1赞 py_ios_dev 6/20/2018
就我而言,此错误是间歇性的。有什么建议吗?stackoverflow.com/questions/50933681/ 中的代码...
1赞 Nicolas Miari 9/19/2018
也许是时候更新这个答案来使用而不是 .compactMap()flatMap()
0赞 mehdigriche 12/25/2019
所以我想最好和最简短的解决方案是“零合并算子”,对吧?
45赞 Duncan C 2/28/2016 #2

这个问题一直出现。这是 Swift 新开发者最难解决的问题之一。

背景:

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

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

将变量声明为 mean(键入 x 或无值)时,在类型后放置一个问号。

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

需要“解包”一个可选值才能获取其中的值。

“!” 运算符是“强制展开”运算符。它说“相信我。我知道我在做什么。我保证当这段代码运行时,变量不会包含 nil。如果你错了,你就会崩溃。

除非你真的知道自己在做什么,否则请避免使用“!”强制展开运算符。对于刚开始的 Swift 程序员来说,这可能是最大的崩溃来源。

如何处理可选选项:

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

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

下面是 optional 绑定的示例,我们的 optional:foo

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
}

Guard 语句是在 Swift 2 中添加的。Guard 允许您在代码中保留“黄金路径”,并避免有时因使用“if let”可选绑定而导致的嵌套 if 级别不断增加。

还有一种称为“零合并运算符”的结构。它采用“optional_var ??replacement_val”。它返回一个非可选变量,其类型与可选变量中包含的数据相同。如果 optional 包含 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 时,假装 “!” 字符不是语言的一部分。这很可能会给你带来麻烦。

评论

7赞 vacawama 2/28/2016
您应该考虑将其设为公共wiki。
3赞 vacawama 2/28/2016
编辑您的答案,右侧的编辑框下方是一个社区 wiki 复选框。你不会再得到代表的答案,但我知道无论如何,这不是一个公然的代表抢夺。
6赞 nhgrif 2/29/2016
鉴于这个问题在网站上被问了一百万次,如果我要花时间发布一个自我回答的问题作为该问题的规范答案,我希望答案远比这更完整。没有关于子句的内容。没有什么比使用哪个结构一样有效了。当我们谈论绑定时,我觉得没有什么值得一提的子句(在许多情况下,它消除了一整层嵌套)。没有关于可选链接的内容。guardif varif letwhereif let
6赞 nhgrif 2/29/2016
当你提到隐式解包的可选选项时,你甚至没有提到你可以使用上述所有可选技巧来安全地解包隐式解包的可选选项。我们也不涉及在同一子句中解开多个选项。
1赞 Duncan C 3/1/2016
@nhgrif,我确实说过“你也可以使用 try/catch 或 guard 错误处理,但通常上面的其他技术之一更干净。你当然有一些好点。这是一个相当大的话题。贡献你自己的答案,涵盖我没有做的事情,而不是发表狙击评论怎么样?这样一来,问题及其答案就成为网站的更好资产。
13赞 2 revs, 2 users 81%QiunCheng #3

首先,您应该知道什么是可选值。 您可以步步操作 The Swift Programming Language 了解详细信息。

其次,您应该知道可选值有两种状态。一个是完整值,另一个是零值。因此,在实现可选值之前,应检查它处于哪种状态。

您可以使用 or 等。if let ...guard let ... else

另一种方法是,如果您不想在实现之前检查变量状态,也可以改用。var buildingName = buildingName ?? "buildingName"

72赞 7 revs, 5 users 78%Sajjon #4

TL;DR答案

除了极少数例外,这条规则是金色的:

避免使用!

声明变量可选 (),而不是隐式解包可选 (IUO) (?!)

换句话说,而是使用:
var nameOfDaughter: String?

而不是:
var nameOfDaughter: String!

使用 或 解包可选变量if letguard let

像这样解包变量:

if let nameOfDaughter = nameOfDaughter {
    print("My daughters name is: \(nameOfDaughter)")
}

或者像这样:

guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: \(nameOfDaughter)")

这个答案旨在简明扼要,为了完全理解,请阅读接受的答案


资源

14赞 Tharzeez #5

由于上面的答案清楚地解释了如何安全地使用 Optionals。 我将尝试解释 swift 中真正的 Optionals 是什么。

声明可选变量的另一种方法是

var i : Optional<Int>

而 Optional 类型只不过是具有两种情况的枚举,即

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

因此,为我们的变量“i”赋值为零。我们可以做或赋值,我们会传递一些值var i = Optional<Int>.nonevar i = Optional<Int>.some(28)

根据斯威夫特的说法,“零”是没有价值。 并且要创建一个初始化的实例,我们必须遵守一个名为 and great 的协议,如果你猜对了,只符合和符合其他类型是不鼓励的。nilExpressibleByNilLiteralOptionalsExpressibleByNilLiteral

ExpressibleByNilLiteral有一个调用的方法,它用 nil 初始化一个 instace。您通常不会调用此方法,并且根据 swift 文档,不建议直接调用此初始值设定项,因为每当您使用文本初始化 Optional 类型时,编译器都会调用它。init(nilLiteral:)nil

甚至我自己也必须把我的头包裹起来(没有双关语的意思)可选:D祝大家快乐

9赞 Wissa #6

当我尝试从 prepare for segue 方法设置我的 Outlets 值时,我曾经遇到过这个错误,如下所示:

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

然后我发现我无法设置目标控制器插座的值,因为控制器尚未加载或初始化。

所以我是这样解决的:

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

目标控制器:

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

我希望这个答案能帮助任何遇到相同问题的人,因为我发现标记的答案是理解可选选项及其工作原理的重要资源,但尚未直接解决问题本身。

评论

0赞 Ahmad F 2/27/2019
只是不要忘记提及在目标控制器;)中调用的位置updateView
0赞 Wissa 2/27/2019
@AhmadF好点子。然而,在这种情况下不需要调用目标控制器,因为我使用变量在 .但是,如果我们进行大量设置,那么创建另一个函数并改为从中调用它肯定会更好。updateViewnamenameLabel.textviewDidLoadviewDidLoad
10赞 6 revsCristik #7

当程序员试图在 Swift 只允许非 nil 值的地方使用 nil 值时,就会发生这种崩溃,方法是告诉编译器相信你那里永远不会有 nil 值,从而允许你的应用程序编译。

有几种情况会导致这种致命错误:

1. 强制展开:

let user = someVariable!

如果为 nil,那么你会得到崩溃。通过执行强制解包,您将 nil 检查责任从编译器转移到了您身上,基本上通过执行强制解包,您可以向编译器保证您永远不会有 nil 值。猜猜如果以某种方式以 nil 值结尾会发生什么。someVariablesomeVariable

溶液?使用可选绑定(又名 if-let),在那里进行变量处理:

if user = someVariable {
    // do your stuff
}

2. 强制(向下)铸造:

let myRectangle = someShape as! Rectangle

在这里,通过强制转换,你告诉编译器不要再担心了,因为你总是有一个实例。只要这种情况成立,您就不必担心。当您或项目中的同事开始循环非矩形值时,问题就开始了。Rectangle

溶液?使用可选绑定(又名 if-let),在那里进行变量处理:

if let myRectangle = someShape as? Rectangle {
    // yay, I have a rectangle
}

3. 隐式解包的可选选项。

假设您有以下类定义:

class User {
    var name: String!
     
    init() {
        name = "(unnamed)"
    }

    func nicerName() {
        return "Mr/Ms " + name
    }
}

现在,如果没有人通过将属性设置为 来弄乱该属性,那么它就会按预期工作,但是,如果从缺少密钥的 JSON 初始化,那么在尝试使用该属性时会出现致命错误。namenilUsername

溶液?不要:)使用它们除非您 102% 确定该属性在需要使用时将始终具有非 nil 值。在大多数情况下,转换为可选或非可选都可以。将其设置为非可选也会导致编译器通过告诉您错过的代码路径来帮助您为该属性提供值

4. 未连接或尚未连接的插座。

这是场景 #3 的一个特殊情况。基本上,您有一些要使用的 XIB 加载的类。

class SignInViewController: UIViewController {
        
    @IBOutlet var emailTextField: UITextField!
}

现在,如果您错过了从 XIB 编辑器连接插座,那么一旦您想要使用插座,应用程序就会崩溃。 溶液?确保所有插座均已连接。或者对它们使用运算符:.或者将 outlet 声明为可选,尽管在这种情况下,编译器会强制您在代码中解开它。?emailTextField?.text = "[email protected]"

5. 视图控制器未从 XIB/Storyboard 加载

所有插座都已正确连接,但视图控制器是实例化的,没有从笔尖/情节提要加载。 在这种情况下,运行时不会连接任何插座。一个典型的例子是 .let myVC = MyViewController()

6. 来自 Objective-C 的值,并且没有可空性注释。

假设我们有以下 Objective-C 类:

@interface MyUser: NSObject
@property NSString *name;
@end

现在,如果没有指定可空性注解(显式或通过 /),则该属性将在 Swift 中导入为(IUO - 隐式解包可选)。一旦某些 swift 代码想要使用该值,如果为 nil,它就会崩溃。NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_ENDnameString!name

溶液?向 Objective-C 代码添加可为 null 性注释。但要注意的是,Objective-C 编译器在可空性方面有点宽容,即使你明确地将它们标记为 .nonnull

3赞 2 revsHoney #8

这是一个更重要的注释,也是为什么在调试值时隐式解包的可选选项可能具有欺骗性。nil

请考虑以下代码: 它编译时没有错误/警告:

c1.address.city = c3.address.city

然而,在运行时,它会给出以下错误:致命错误:解包可选值时意外发现 nil

你能告诉我哪个对象是吗?

你不能!

完整的代码是:

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
}

长话短说,通过使用你隐藏了变量可能来自其他读者的可能性。当它崩溃时,你会想“什么鬼?!我的不是可选的,那我为什么崩溃?!.var address : Address!niladdress

因此,最好这样写:

c1.address.city = c2.address!.city  // ERROR:  Fatal error: Unexpectedly found nil while unwrapping an Optional value 

你现在能告诉我是哪个物体是零吗

这一次,代码对你来说已经更清楚了。您可以合理化并认为可能是被强行解开的参数。address

完整的代码是:

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
}
3赞 2 revs, 2 users 67%Ale Mohamad #9

当您声明了 ,但未连接到情节提要时,错误和出现次数最多。EXC_BAD_INSTRUCTIONfatal error: unexpectedly found nil while implicitly unwrapping an Optional value@IBOutlet

您还应该了解可选选项的工作原理,在其他答案中提到过,但这是我唯一一次出现在我面前。

评论

0赞 pkamb 12/9/2019
此错误的原因不应该有致命错误:在隐式解开错误的可选值版本时意外发现 nil@IBOutlet
1赞 Ale Mohamad 12/11/2019
这是真的。也许当我发送答案时,这就是我的意思,我复制粘贴了第一条致命的错误消息。哈米什的答案看起来非常完整。
1赞 Mike Volmar #10

我在从表视图控制器到视图控制器进行分段时遇到了这个错误,因为我忘记在主情节提要中指定视图控制器的自定义类名。

一些简单的东西,如果其他一切看起来都正常,值得检查

3赞 ShigaSuresh #11

如果您在 CollectionView 中收到此错误,请尝试创建 CustomCell 文件和 Custom xib。

在 mainVC 的 ViewDidLoad() 中添加此代码。

    let nib = UINib(nibName: "CustomnibName", bundle: nil)
    self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")
1赞 Quick learner #12

如果我的情况是,我将一个变量设置为 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)
    }
    

}
1赞 umairhhhs #13

简单来说 您正在尝试使用可选变量的值,该值为 NIL。 可以使用快速修复,也可以代替强制展开,例如放在变量的末尾guardif let!

2赞 K-Soliman #14

Xcode 12 iOS的14 Swift 5

我的问题是导航类型,因为我在没有实例化情节提要的情况下直接调用了 vie 控制器,因此这意味着尚未从情节提要设置数据。

导航时,使用

let homeViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "home") as? HomeEventsViewController
    homeViewController?.modalTransitionStyle = .crossDissolve
    homeViewController?.modalPresentationStyle = .fullScreen
    view.present(homeViewController ?? UIViewController(), animated: true, completion: nil)

希望它有效:-)

0赞 Nathan Day #15

这是因为你试图使用一个可能为零的值,但你决定你不想检查它,而是在你使用它时假设它的集合并将其定义为 !,使用变量集作为强制展开有不同的哲学,有些人根本不反对在那里使用, 我个人认为它们可以用于总是崩溃并且易于推理的事情,通常是对资源的引用,例如对 xib 文件的出口,或者在您的应用程序中使用作为您资产一部分的图像,如果这些设置不正确,您的应用程序将立即崩溃, 出于一个非常明显的原因,当正在创建的对象的顺序不确定时,您可能会陷入困境,并且试图对此进行推理解决方案可能很困难,这通常意味着糟糕的设计,因为即使您将它们设置为可选,对可选变量的调用可能永远不会执行,出于安全原因,某些项目可能要求使用强制展开, 像银行应用程序这样的东西,因为他们希望应用程序崩溃,而不是继续以计划外的方式工作。

1赞 udi #16

斯威夫特 5.7 +

if let隐藏现有可选变量的简写

以上答案清楚地解释了出现此问题的原因以及如何处理此问题。但是,从现在开始,有一种新的方法来处理这个问题。swift 5.7+

var myVariable : Int?

以前

if let myVariable = myVariable {
    //this part get executed if the variable is not nil
}else{
    //this part get executed if the variable is nil
}

现在

现在我们可以省略表达式的右侧。

if let myVariable {
    //this part get executed if the variable is not nil
}else{
    //this part get executed if the variable is nil
}

以前,我们必须重复引用的标识符两次,这可能会导致这些可选绑定条件很冗长,尤其是在使用冗长的变量名称时。

但现在有一个用于可选绑定的速记语法,即省略表达式的右侧。

同样的事情也适用于语句。guard let

更多详情

if-let 速记提案

-1赞 2 revsAndrew_STOP_RU_WAR_IN_UA #17

这么久的其他答案......

您只需要知道存在可为 null 的对象。而且它们可能没有价值(“无”)。错误意味着您试图在有 nil 的那一刻获取变量的值,但不是该值。

可以肯定的是,您在代码行上遇到了此错误,其中包含“!”,这意味着“我保证这个可为 null 的值不是 nil,只需使用它。

避免此问题的方法:

  1. 的用法 ?
// get someClassParam in case of someNullableClassInstance is not nil
let a = someNullableClassInstance?.someClassParam
  1. 检查里面是否有值:
if let someNullableClassInstance = someNullableClassInstance {
    //here is someNullableClassInstance for sure is not nil
}

//or

// this works the same as previous "if" code
if let someNullableClassInstance {
    //here is someNullableClassInstance for sure is not nil
}

//or

guard let someNullableClassInstance = someNullableClassInstance else { return }
//from this string someNullableClassInstance IS NOT NULLABLE!

//or

// this works the same as previous "guard" code
guard let someNullableClassInstance else { return }
//from this string someNullableClassInstance IS NOT NULLABLE!
  1. 使用默认值:
// ?? means "assign value in case of nil"
let someBoolParam = someNullableClassInstance?.someBoolParam ?? false

请记住:使用“!”是非常糟糕的做法。

0赞 codrut #18

在 Realm 上下文中,如果它失败了,那么你新创建的 Realmable 类/结构就没有注册。NSClassFromString(realmClass)

在应用生命周期的早期调用 (appDidFinishLaunching)Realm.registerRealmables(MyClass.self, MyOtherClass.self,...)

1赞 Michel Storms #19

我现在在 Xcode 15 中没有明显的原因发生过几次这种情况。 签出另一个分支(例如,main),然后再次签出你的工作台似乎可以修复它。不知道为什么...