如何使用guard语句在赋值后检测nil



我正在尝试使用guard语句来检查nil。

我很困惑为什么下面的让它通过并生成BAD_EXEC错误:

    guard let event:Event! = eventsImagesLoading.removeValueForKey(location) else {
        return
    }
    images[location] = responseData
    event!.iconImgData = images[location]

我试图在方法调用后检查"event"是否为零。如果是的话,它应该返回。但事实上,它在活动中滑了过去并崩溃了!。iconImageData。。。线

其他答案向您展示了如何解决问题,但并没有真正解释为什么会出现这种错误,所以我想我会参与其中。


guard let ... else语句与if let ...非常相似,它试图将一个可选的(通常只要不是nil)的未包装值绑定到同一底层类型的非可选不可变值;使用可选绑定

var a: Int? = 5
if let b = a {
    // b unwrapped, type inferred to non-optional type Int
    print(b) // "5"
}

如果a的值为nil,则上述绑定将失败,因为默认情况下(通过类型推断),b的类型为Int,不能容纳nil

在这种情况下,显式声明b为隐式展开的可选项是没有意义的,因为即使a为零,这也将允许成功绑定。等效的非感测块将显式声明b为可选,此后可选aInt?)到可选bInt?)的"尝试的可选绑定"自然总是成功的,并且if let ...块减少为完全冗余的块本地分配。

a = nil
if let b: Int! = a {
    print(b) // "nil"
    // wups, we managed to bind a to b even though a is nil ...
    // note also this peculiarity
    print(b.dynamicType) // Optional<Int>
    let c: Int! = nil
    print(c.dynamicType) // ImplicitlyUnwrappedOptional<Int>
}
if let b: Int? = a {
    print(b) // nil
    // wups, we managed to bind a to b even though a is nil ...
}

请注意,无论我们显式指定b为类型Int?(可选)还是类型Int!(隐式展开可选),传递到if let块的绑定的不可变b在这两种情况下都只是常规可选(类型Int?)。这就解释了为什么需要在guard let子句之后展开eventevent!.iconImgData,尽管我们声明它是隐式展开的类型。

因此,在您的示例中,guard let ... else语句将不会捕获eventsImagesLoading.removeValueForKey(location)nil的情况,因为绑定到event(它是隐式展开的可选类型Event!)将成功,即使对于nil的情况也是如此。

func foo() {
    let bar : Int? = nil
    guard let _: Int! = bar else {
        print("this is surely nil")
        return
    }
    print("but an implicitly unwrapped optional will fall through")
}
foo() // "but an implicitly unwrapped optional will fall through"

对于需要延迟初始化的免疫表,通常只应使用隐式展开选项(值nil,直到初始化)。在隐式展开的可选项初始化后,其值不应为nil(而在上面的示例中,通过可选绑定初始化后,它的值仅为nil)。

此外,您通常应该让编译器推断出您试图在guard let INFER_THIS = ... elseif let INFER_THIS = ...子句中绑定到的不可变的非可选类型。


我们可以考虑是否应该允许它使用可选绑定可选类型(保证成功),但这是另一个讨论。

Event!更改为Event(第1行),将event!更改为event(第5行)。

guard let event:Event = eventsImagesLoading.removeValueForKey(location) else {
        return
    }
    images[location] = responseData
    event.iconImgData = images[location]