致命错误:在打开一个可选值时意外地发现nil;的意思吗?

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

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

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


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

576478 次浏览

背景:什么是可选选项?

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

可选类型是泛型类型,这意味着Optional<Int>Optional<String>是不同的类型——<>中的类型称为Wrapped类型。在底层,Optional是一个枚举,有两种情况:.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中的也许, Rust中的选项, c++ 17中的可选

在编程语言的没有选项类型中,通常使用特定的“sentinel"价值来表示缺少有效值。例如,在Objective-C中,nil (空指针)表示缺少对象。对于像int这样的基本类型,不能使用空指针,所以你需要一个单独的变量(如value: IntisValid: Bool)或一个指定的哨兵值(如-1INT_MIN)。这些方法很容易出错,因为很容易忘记检查isValid或检查哨兵值。此外,如果一个特定的值被选择为哨兵,这意味着它不能再被视为int0值。

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


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

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

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

crash line

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

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

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

特别是iboutlet,通常是隐式地展开的可选选项。这是因为你的xib或storyboard将在运行时连接outlet, 初始化。因此,您应该确保在加载outlet之前没有访问它们。你还应该检查你的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")
}

这样做的目的是首先检查可选项是否包含值。如果它是,那么' unwrapped '值被赋给一个新变量(number)——然后你可以自由地使用它,就像它是非可选的一样。如果可选的包含一个值,那么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 condition else {...}定义。

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

guard let number = anOptionalInt else {
return
}

(注意,在守卫体中,必须使用控制转移语句,以退出当前执行代码的作用域)。

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

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

例如:

guard let number = anOptionalInt else {
return
}


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

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

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

无合并运算符

无合并运算符三元条件运算符的简洁版本,主要用于将可选对象转换为非可选对象。它的语法是a ?? b,其中a是可选类型,而b是与a相同的类型(尽管通常是非可选类型)。

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

let number = anOptionalInt ?? 0

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

它只是缩写:

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

可选的链接

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

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

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

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

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

let optionalQux = foo?.baz?.qux

同样,因为foobaz是可选的,所以不管qux本身是否可选,从qux返回的值总是可选的。

mapflatMap

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

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

let anOptionalString:String?

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

因为stringByAppendingString接受一个非可选字符串参数,所以不能直接输入可选字符串。然而,通过使用map,我们可以使用如果anOptionalString有值,则允许使用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块中看到的error常量并没有由我们声明——它是由catch自动生成的。

你也可以自己声明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?,在极少数情况下,处理错误并不重要。


资源

这个问题在SO上一直以来出现。这是Swift新开发人员首先要解决的问题之一。

背景:

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

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

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

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

可选选项需要“打开”;为了获取里面的值。

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

除非你真的知道你在做什么,否则避免使用“;!强制展开运算符。对于初学者来说,它可能是最大的崩溃来源。

如何处理可选选项:

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

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

下面是一个可选绑定的例子,其中foo是可选的:

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

请注意,使用可选投标时定义的变量只存在于if语句体中(仅是"in scope")。

或者,你可以使用一个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的级别,这有时会导致使用&;if let&;可选的绑定。

还有一种结构叫做“无合并运算符”。它的形式是"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时,假装“;!”文字不是语言的一部分。这很可能会给你带来麻烦。

首先,你应该知道什么是Optional值。

其次,您应该知道可选值有两个状态。一个是全值,另一个是空值。因此,在实现一个可选值之前,应该检查它是哪个状态。

你可以使用if let ...guard let ... else等等。

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

TL;博士回答

对于很少有例外,这个规则是黄金规则:

避免使用!

声明变量为可选变量(?),而不是隐式打开可选变量(IUO) (!)

换句话说,应该使用:
var nameOfDaughter: String? < / p > < p >而不是:< br > var nameOfDaughter: String! < / p >

使用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)")

这个答案是为了简洁,要完全理解,请阅读已接受的答案


资源

因为上面的答案清楚地解释了如何安全地使用可选选项。 我将尝试解释什么是可选的真正在swift

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

var i : Optional<Int>

和可选类型只是一个枚举与两种情况,即

 enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
.
.
.
}
将nil赋值给变量i。我们可以 var i = Optional<Int>.none 或者为了赋值,我们会传递一些值 var i = Optional<Int>.some(28) < / p >

根据swift, 'nil'是没有值。 要创建一个用nil初始化的实例,我们必须符合一个名为ExpressibleByNilLiteral的协议,如果你猜到了,那就太好了,只有Optionals符合ExpressibleByNilLiteral,不鼓励符合其他类型

ExpressibleByNilLiteral有一个名为init(nilLiteral:)的方法,它用nil初始化一个实例。你通常不会调用这个方法,根据swift文档,不建议直接调用这个初始化式,因为每当你用nil文字初始化一个Optional类型时,编译器都会调用它。

甚至我自己也不得不把(没有双关语的意思)我的脑袋围绕在可选选项:D 快乐swift所有

当我试图从prepareforsegue方法中设置outlet值时,我有过这样的错误:

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
}

我希望这个答案能帮助那些有同样问题的人,因为我发现标记的答案对理解可选选项及其工作方式是很好的资源,但并没有直接解决问题本身。

基本上,你试图在Swift只允许非nil值的地方使用nil值,通过告诉编译器信任你,那里永远不会有nil值,从而允许你的应用程序编译。

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

  1. < p >迫使非覆盖:

    let user = someVariable!
    

    如果someVariable为nil,那么你会得到一个崩溃。通过强制展开,你把nil检查责任从编译器转移到你身上,基本上通过强制展开,你向编译器保证那里永远不会有nil值。猜猜如果一个nil值以某种方式结束在someVariable中会发生什么?

    解决方案吗?使用可选绑定(又名if-let),在那里做变量处理:

    if user = someVariable {
    // do your stuff
    }
    
  2. forced (down)casts:

    let myRectangle = someShape as! Rectangle
    

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

    解决方案吗?使用可选绑定(又名if-let),在那里做变量处理:

    if let myRectangle = someShape as? Rectangle {
    // yay, I have a rectangle
    }
    
  3. Implicitly unwrapped optionals. Let's assume you have the following class definition:

    class User {
    var name: String!
    
    
    init() {
    name = "(unnamed)"
    }
    
    
    func nicerName() {
    return "Mr/Ms " + name
    }
    }
    

    现在,如果没有人通过将name属性设置为nil来破坏它,那么它将按预期工作,然而,如果User是从缺少name键的JSON初始化的,那么在尝试使用该属性时将会得到致命错误。

    解决方案吗?不要使用它们:)除非你102%确定该属性在需要使用时总是有一个非nil值。在大多数情况下,转换为可选或非可选都可以。将其设置为非可选属性还将导致编译器帮助你,告诉你错过的代码路径,并为该属性

    提供一个值
  4. 未连接或尚未连接的出口。这是场景#3的一个特殊情况。基本上,您有一些想要使用的装载xib的类。

    class SignInViewController: UIViewController {
    
    
    @IBOutlet var emailTextField: UITextField!
    }
    
    现在如果你错过了从XIB编辑器连接出口,那么应用程序将在你想要使用出口的时候崩溃。 解决方案吗?确保所有插座都已连接。或者对它们使用?操作符:emailTextField?.text = "my@email.com"。或者将outlet声明为可选,尽管在这种情况下,编译器将强制您在整个代码中展开它
  5. 来自Objective-C的值,并且没有可空性注释。让我们假设我们有以下Objective-C类:

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

    现在,如果没有指定可空性注释(显式地或通过NS_ASSUME_NONNULL_BEGIN/NS_ASSUME_NONNULL_END),则name属性将在Swift中作为String!导入(IUO -隐式打开包装的可选选项)。一旦一些swift代码想要使用这个值,如果name为nil,它就会崩溃。

    解决方案吗?在Objective-C代码中添加可空性注释。不过要注意,Objective-C编译器在涉及可空性时有点允许,你可能最终得到nil值,即使你显式地将它们标记为nonnull

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

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

c1.address.city = c3.address.city

但在运行时,它会给出以下错误:致命错误:在打开可选值时意外地发现nil

你能告诉我哪个对象是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!,你可以隐藏变量可以从其他读取器nil的可能性。当它崩溃的时候,你会想“搞什么鬼?!”我的address不是一个可选的,所以为什么我崩溃?!

因此,最好这样写:

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

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

这一次,代码对您来说更清楚了。你可以合理地认为可能是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
}

当你声明了一个@IBOutlet,但没有连接到故事板时,错误EXC_BAD_INSTRUCTIONfatal error: unexpectedly found nil while implicitly unwrapping an Optional value出现的次数最多。

你还应该了解可选是如何工作的,在其他答案中提到过,但这是唯一一次出现在我身上。

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

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

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

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

    let nib = UINib(nibName: "CustomnibName", bundle: nil)
self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")

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



}

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

的末尾

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)

希望它能起作用:-)

这是因为你想使用一个值可可能是零,但是你决定你不想要检查它,而是假设其设置当你使用它,它定义为!,有不同的哲学在使用武力的变量设置为打开,一些人反对使用,我个人认为他们是好事情会崩盘,原因简单,通常引用的资源,像媒体xib文件,或使用的图片与你应用程序的一部分资产,如果这些都不是设置得当,你的应用程序会马上崩溃,很明显的原因,你可以进入困难当对象被创建的顺序可能不确定,并试图原因解决方案是很困难的,这通常意味着一个糟糕的设计,甚至你让他们可选的,调用您可选变量可能永远不会被执行,一些项目可以要求使用武力的非覆盖出于安全原因,比如银行应用程序,因为他们希望应用程序崩溃,而不是以一种计划外的方式继续工作。

Swift 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简写的提议