ikevin8me
ikevin8me

Reputation: 4313

How to use guard statement to detect nil after an assignment?

I'm trying to use guard statement to check against nil.

I'm puzzled why the following let it slip through and generate a BAD_EXEC error:

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

I'm trying to check if "event" is nil after the method call. If it is it should just return. But in reality it slips through and crashes on the event!.iconImageData... line.

Upvotes: 1

Views: 1882

Answers (3)

Vatsal
Vatsal

Reputation: 18171

Change Event! to Event (line 1) and event! to event (line 5).

Upvotes: 3

dfrib
dfrib

Reputation: 73176

The other answers show you how to solve your issue, but doesn't really explain why this error occurs, so I thought I'd pitch in on that.


The guard let ... else statement, much like if let ..., attempts to bind the unwrapped value of an optional---generally as long as this is not nil---to a non-optional immutable of the same underlying type; using optional binding

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

The above binding would fail if a had the value nil, as b, as per default (by type inference), is of type Int which cannot hold nil.

In this context, it makes no sense to explicitly declare b to be an implicitly unwrapped optional, as this will allow a successful binding even if a is nil. An equivalently non-sense block would be to explicitly declare b to be an optional, whereafter the "attempted optional binding" of optional a (Int?) to optional b (Int?) would naturally always succeed, and the if let ... block reduces to a completely redundant block-local assignment.

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

Note the peculiarity that no matter if we explicitly specify b to be of type Int? (optional) or type Int! (implicitly unwrapped optional), the binded immutable b passing into the if let block is, for both cases, just a regular optional (of type Int?). This explains why you need to unwrap event (event!.iconImgData) after the guard let clause, even though we declared it to be of an implicitly unwrapped type.

Hence, in your example, your guard let ... else statement will not catch cases where eventsImagesLoading.removeValueForKey(location) is nil, as the binding to event (which is of implicitly unwrapped optional type Event!) will success even for nil cases.

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"

You should generally only use implicitly unwrapped optionals for immutables where you need delayed initialization (value nil until initialized). After initialization of an implicitly unwrapped optional, its value should never be nil (whereas in the example above, it's value is, after initialization by optional binding, just that; nil).

Also, you should generally let the compiler infer the non-optional type of the immutable to which you are trying to bind in the guard let INFER_THIS = ... else or if let INFER_THIS = ... clauses.


We could ponder over whether it should even be allowed to use optional binding to optional types (guaranteed success), but that is another discussion.

Upvotes: 5

Muzahid
Muzahid

Reputation: 5186

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

Upvotes: 1

Related Questions