Martin Muldoon
Martin Muldoon

Reputation: 3438

Explicitly unwrapping optional nil does not cause crash

A few folks asked this question before, yet no answer was accepted.

I have a UITableViewCell that contains a UITextField.

If I click slightly outside of the textField the row highlights. I want to prevent this.

To prevent it I do the following:

override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath! {
    return nil
}

This works perfectly fine. Here is my question. The author of the tutorial states:

Note that returning nil from a method is only allowed if there is a question mark (or exclamation point) behind the return type.

What does he mean that you can put an exclamation mark behind the optional return type? Why doesn't the program crash? Returning nil after I place an exclamation mark after the IndexPath return type doesn't crash. I thought ! would explicitly unwrap the nil and fail?

Upvotes: 6

Views: 611

Answers (2)

Ratnesh Jain
Ratnesh Jain

Reputation: 681

One way to think this as a choise of the API Implementor. If implementor handles the input arguments it will not be any problem to the API User.

Lets create a drawing text class which just prints at console.

class TextDrawer {

  var mustBeText: String!

  func renderText(string: String) {
      print(string)
  }

  func renderSafely() {
     renderText(string: self.mustBeText ?? "Nothing found to be rendered")
     // or 
     if let safeString = self.mustBeText {
        renderText(string: safeString)
     }
  }

  func renderUnsafely() {
      renderText(string: mustBeText)
  }
}

We have defined the mustBeText as String! means we are allowed to expect any string as well as nil argument.

Now, as we create a instance of the class as below:

let textDrawer = TextDrawer()
textDrawer.mustBeText = nil // this is allowed since the `mustBeText` is `String!`.

textDrawer.renderSafely() // prints -- Nothing found to be rendered

textDrawer.renderUnsafely() // crashes at runtime.

The renderUnsafaly() will crash since its not handling the nil case.

Upvotes: 1

Martin R
Martin R

Reputation: 539965

As of Swift 3, “Implicitly unwrapped optional” is not a separate type, but an attribute on the declaration of a regular/strong optional. For the details, see SE-0054 Abolish ImplicitlyUnwrappedOptional type.

A function with an IUO return type can return nil, and assigning the return value to a variable makes that a regular optional:

func foo() -> Int! {
    return nil
}

let x = foo() // Type of `x` is `Int?`
print(x) // nil

Only if evaluation as an optional is not possible then the value will be forced-unwrapped (and cause a runtime exception is the value is nil):

let y = 1 + foo() // Fatal error: Unexpectedly found nil while unwrapping an Optional value

In your case, your

override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath!

method overrides the UITableViewController method

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath?

and can return nil. This does not crash unless the caller unwraps the value.


Remark: The above is meant as an explanation why your code compiles and works. Generally, I do not see a good reason to use implicitly unwrapped optional return types. The main use-cases of IUOs are stated in SE-0054:

The ImplicitlyUnwrappedOptional ("IUO") type is a valuable tool for importing Objective-C APIs where the nullability of a parameter or return type is unspecified. It also represents a convenient mechanism for working through definite initialization problems in initializers.

Upvotes: 6

Related Questions