我的Swift程序崩溃与EXC_BAD_INSTRUCTION和以下类似错误之一。这个错误是什么意思,我该如何修复它?
致命错误:在打开可选值时意外地发现nil
or
致命错误:在隐式地展开可选值时意外地发现nil
这篇文章旨在收集“意外发现为零”问题的答案,这样它们就不会分散而难以找到。请随意添加您自己的答案或编辑现有的wiki答案。
我的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应用程序崩溃
这个问题在《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时,假装“!”字符不是语言的一部分。这很可能会给你带来麻烦。
首先,您应该知道什么是Optional值。 详细信息请参见《Swift编程语言》。
其次,您应该知道可选值有两个状态。一个是全值,另一个是空值。因此,在实现一个可选值之前,应该检查它是哪个状态。
你可以用if let…或者守卫让…Else等等。
还有一种方法,如果你不想在实现之前检查变量的状态,你也可以使用var buildingName = buildingName ??“buildingName”。
TL;博士回答
除了极少数例外,这条规则是黄金法则:
避免使用!
声明变量是可选的(?),而不是隐式地打开可选的(IUO) (!)
换句话说,应该使用: var nameOfDaughter:字符串?
而不是: var nameOfDaughter:字符串!
使用if let或guard let展开可选变量
要么像这样展开变量:
if let nameOfDaughter = nameOfDaughter {
print("My daughters name is: \(nameOfDaughter)")
}
或者像这样:
guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: \(nameOfDaughter)")
这个答案是简洁的,为了充分理解阅读接受的答案
资源
避免强制展开
因为上面的答案清楚地解释了如何安全地玩可选项目。 我将试着解释什么是可选的真正在迅速。
声明可选变量的另一种方法是
在 : 可选<Int>
和可选类型只是一个枚举与两种情况,即
enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
.
.
.
}
将nil赋值给变量i。我们可以 var i =可选<Int>.无 或者为了赋值,我们会传递一些值 var i =可选<Int>.some(28)
根据swift, 'nil'是没有值。 为了创建一个用nil初始化的实例,我们必须遵守一个叫做expressiblebynillliteral的协议,如果你猜到了,很好,只有optional符合expressiblebynillliteral,不鼓励符合其他类型。
expressiblebynilleral有一个叫做init(nilleral:)的方法,它用nil初始化实例。你通常不会调用这个方法,根据swift文档,不建议直接调用这个初始化式,因为每当你初始化一个可选类型时,编译器都会调用它,用nil文字。
就连我自己也不得不(没有双关语的意思)把我的脑袋绕在可选科目上:D 快快乐乐。
当我试图从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值,从而允许你的应用程序编译。
有几种情况会导致这种致命错误:
forced unwraps: let user = someVariable! If someVariable is nil, then you'll get a crash. By doing a force unwrap you moved the nil check responsibility from the compiler to you, basically by doing a forced unwrap you're guaranteeing to the compiler that you'll never have nil values there. And guess what it happens if somehow a nil value ends in in someVariable? Solution? Use optional binding (aka if-let), do the variable processing there: if user = someVariable { // do your stuff } forced (down)casts: let myRectangle = someShape as! Rectangle Here by force casting you tell the compiler to no longer worry, as you'll always have a Rectangle instance there. And as long as that holds, you don't have to worry. The problems start when you or your colleagues from the project start circulating non-rectangle values. Solution? Use optional binding (aka if-let), do the variable processing there: if let myRectangle = someShape as? Rectangle { // yay, I have a rectangle } 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 } } Now, if no-one messes up with the name property by setting it to nil, then it works as expected, however if User is initialized from a JSON that lacks the name key, then you get the fatal error when trying to use the property. Solution? Don't use them :) Unless you're 102% sure that the property will always have a non-nil value by the time it needs to be used. In most cases converting to an optional or non-optional will work. Making it non-optional will also result in the compiler helping you by telling the code paths you missed giving a value to that property Unconnected, or not yet connected, outlets. This is a particular case of scenario #3. Basically you have some XIB-loaded class that you want to use. class SignInViewController: UIViewController { @IBOutlet var emailTextField: UITextField! } Now if you missed connecting the outlet from the XIB editor, then the app will crash as soon as you'll want to use the outlet. Solution? Make sure all outlets are connected. Or use the ? operator on them: emailTextField?.text = "my@email.com". Or declare the outlet as optional, though in this case the compiler will force you to unwrap it all over the code. Values coming from Objective-C, and that don't have nullability annotations. Let's assume we have the following Objective-C class: @interface MyUser: NSObject @property NSString *name; @end Now if no nullability annotations are specified (either explicitly or via NS_ASSUME_NONNULL_BEGIN/NS_ASSUME_NONNULL_END), then the name property will be imported in Swift as String! (an IUO - implicitly unwrapped optional). As soon as some swift code will want to use the value, it will crash if name is nil. Solution? Add nullability annotations to your Objective-C code. Beware though, the Objective-C compiler is a little bit permissive when it comes to nullability, you might end up with nil values, even if you explicitly marked them as 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地址:地址!您隐藏了变量可以为nil的可能性,不让其他读取器看到。当它崩溃的时候,你会想“搞什么鬼?!”我的地址不是可选的,所以我为什么要崩溃?!
因此,最好这样写:
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
}
错误EXC_BAD_INSTRUCTION和致命错误:在隐式地展开Optional值时意外地发现nil,在声明@IBOutlet但没有连接到故事板时出现的最多。
你还应该了解其他答案中提到的可选选项是如何工作的,但这是唯一一次出现在我身上。
我在从表视图控制器到视图控制器进行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)
}
}
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)
希望它能起作用:-)
This is because you are trying to use a value which can possible be nil, but you decided you don't want to have to check it, but instead assume its set when you uses it and define it as !, there are different philosophies on use of variable set as force unwrap, some people are against there use at all, I personal think they are ok for things that will crash all the time and are simple to reason about, usually references to resource, like outlets to xib files, or uses of images with you app that are part of your assets, if these are not set up properly, you app is going to crash straight away, for a very obvious reason, you can get into difficult when the order of objects being created can be uncertain, and trying to reason solutions to this can be difficult, it usually means a bad design as even it you make them optional, calls to you optional variable may not ever be executed, some projects can demand use of force unwraps for security reasons, things like banking apps, because they want the app to crash rather then continue to work in an unplanned way.
Swift 5.7 +
如果让简写的影子一个现有的可选变量
以上回答清楚地解释了为什么会出现这个问题以及如何处理这个问题。但是从swift 5.7+开始,有了一种新的方式来处理这个问题。
是我的变量:整数?
以前
if let myVariable = myVariable {
//this part get executed if the variable is not nil
}else{
//this part get executed if the variable is nil
}
now
现在我们可以省略表达式的右边。
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简写的建议