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

Swift 什么是;致命错误:在展开可选值“时意外发现nil”;什么意思?,swift,exception,error-handling,Swift,Exception,Error Handling,我的Swift程序因EXC\u BAD\u指令和以下类似错误之一而崩溃。这个错误是什么意思?我该如何修复它 致命错误:在展开可选值时意外发现nil 或 致命错误:隐式展开可选值时意外发现nil 这篇文章旨在收集“意外发现零”问题的答案,这样它们就不会分散和难以找到。请随意添加您自己的答案或现有的wiki答案。此答案是。如果你觉得可以做得更好,请随意 背景:什么是可选的? 在Swift中,是一个:它可以包含原始(“包装”)类型中的任何值,或者根本不包含任何值(特殊值nil)。可选值必须是展开的,

我的Swift程序因
EXC\u BAD\u指令
和以下类似错误之一而崩溃。这个错误是什么意思?我该如何修复它

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

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


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

此答案是。如果你觉得可以做得更好,请随意

背景:什么是可选的? 在Swift中,是一个:它可以包含原始(“包装”)类型中的任何值,或者根本不包含任何值(特殊值
nil
)。可选值必须是展开的,才能使用

Optional是a,这意味着
Optional
Optional
是不同的类型,
中的类型称为包装类型。在发动机罩下,可选的是一个带有两个箱子的箱子:
。一些(包装)
。无
,其中
。无
相当于

if anOptionalInt != nil {
    print("Contains a value!")
} else {
    print("Doesn’t contain a value.")
}
Optionals可以使用命名类型
Optional
,或者(最常见的)作为带有
后缀的速记来声明

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
致命错误:在展开可选值时意外发现nil

由于此处的
anOptionalString
nil
,您将在强制展开该字符串的行上发生崩溃

2.隐式展开选项 这些是用
定义的,而不是类型后面的

var optionalDouble: Double!   // this value is implicitly unwrapped wherever it's used
假设这些选项包含一个值。因此,每当您访问隐式展开的可选项时,它将自动为您强制展开。如果它不包含值,它将崩溃

print(optionalDouble) // <- CRASH
然而,99.9%的时间在使用optionals时,如果它包含一个值,您实际上会想要访问它包含的值。为此,可以使用可选绑定

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

例如:

let anOptionalString: String?
print(anOptionalString!) // <- CRASH
if let number = anOptionalInt {
    print("Contains a value! It is \(number)!")
} else {
    print("Doesn’t contain a number")
}
guard let number = anOptionalInt else {
    return
}

print("anOptionalInt contains a value, and it’s: \(number)!")
foo?.bar = Bar()
if (foo?.bar = Bar()) != nil {
    print("bar was set successfully")
} else {
    print("bar wasn’t set successfully")
}
var anOptionalString:String? = "bar"

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

print(anOptionalString) // Optional("foobar")
var anOptionalString:String?

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

print(anOptionalString) // nil
do {
    let result = try someThrowingFunc()    
} catch let error as NSError {
    print(error.debugDescription)
}
这样做的目的是首先检查可选项是否包含值。如果是,则将“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语句

允许您定义成功的条件,并且当前作用域只有在满足该条件时才会继续执行。它们是用语法
guard condition else{…}
定义的

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

guard let number = anOptionalInt else {
    return
}
(请注意,在防护体中,您必须使用其中一个以退出当前执行代码的范围)

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

guard语句真正巧妙的地方在于,现在可以在语句后面的代码中使用unwrapped值(正如我们所知,未来的代码只能在可选值有值的情况下执行)。这对于消除嵌套多个if语句所创建的错误非常有用

例如:

let anOptionalString: String?
print(anOptionalString!) // <- CRASH
if let number = anOptionalInt {
    print("Contains a value! It is \(number)!")
} else {
    print("Doesn’t contain a number")
}
guard let number = anOptionalInt else {
    return
}

print("anOptionalInt contains a value, and it’s: \(number)!")
foo?.bar = Bar()
if (foo?.bar = Bar()) != nil {
    print("bar was set successfully")
} else {
    print("bar wasn’t set successfully")
}
var anOptionalString:String? = "bar"

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

print(anOptionalString) // Optional("foobar")
var anOptionalString:String?

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

print(anOptionalString) // nil
do {
    let result = try someThrowingFunc()    
} catch let error as NSError {
    print(error.debugDescription)
}
Guards还支持与if语句支持的相同的整洁技巧,例如同时展开多个选项并使用
where
子句

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

零凝聚算子 是的一个漂亮的速记版本,主要用于将可选项转换为非可选项。它的语法是
a??b
,其中
a
为可选类型,
b
a
为同一类型(尽管通常为非可选)

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

let number = anOptionalInt ?? 0
let newFoo = foo ?? "nil" // "??" is the nil coalescing operator
print("foo = \(newFoo)")
if let nameOfDaughter = nameOfDaughter {
    print("My daughters name is: \(nameOfDaughter)")
}
guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: \(nameOfDaughter)")
这将定义一个
Int
类型的
number
常量,该常量将包含
anOptionalInt
的值(如果它包含值),否则将包含
0

这只是以下的简写:

let number = anOptionalInt != nil ? anOptionalInt! : 0
可选链 您可以使用以调用方法或访问可选对象上的属性。使用变量名时,只需在变量名后面加一个

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

var foo : Foo?
如果我们想在
foo
上调用一个不返回的方法
if let nameOfDaughter = nameOfDaughter {
    print("My daughters name is: \(nameOfDaughter)")
}
guard let nameOfDaughter = nameOfDaughter else { return }
print("My daughters name is: \(nameOfDaughter)")
 enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none 
    case some(Wrapped)
    .
    .
    .
}
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
}
let user = someVariable!
if user = someVariable {
    // do your stuff
}
let myRectangle = someShape as! Rectangle
if let myRectangle = someShape as? Rectangle {
    // yay, I have a rectangle
}
class User {
    var name: String!

    init() {
        name = "(unnamed)"
    }

    func nicerName() {
        return "Mr/Ms " + name
    }
}
class SignInViewController: UIViewController {

    @IBOutlet var emailTextField: UITextField!
}
@interface MyUser: NSObject
@property NSString *name;
@end
c1.address.city = c3.address.city
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
}
c1.address.city = c2.address!.city  // ERROR:  Fatal error: Unexpectedly found nil while unwrapping an Optional value 
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
}
    let nib = UINib(nibName: "CustomnibName", bundle: nil)
    self.collectionView.register(nib, forCellWithReuseIdentifier: "cell")