Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicitly unwrapping optional nil does not cause crash

Tags:

swift

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?

like image 361
Martin Muldoon Avatar asked Mar 29 '18 15:03

Martin Muldoon


People also ask

What will happen if you try to unwrap an optional that contains nil like so?

Unwrap an optional type with the nil coalescing operator If a nil value is found when an optional value is unwrapped, an additional default value is supplied which will be used instead. You can also write default values in terms of objects.

What kind of error occurs when you force unwrap an optional that contains nil?

It's called implicitly unwrapped since Swift force unwraps it every time. The drawback of this is same as forced unwrapping - if the value is nil when accessing, it leads to a fatal error. Similar to optionals, optional binding and optional chaining can also be used for implicitly unwrapped optionals.

Why use implicitly unwrapped optional?

Implicitly unwrapped optionals are a compromise between safety and convenience. If you declare a property as optional, then you need to use optional binding or optional chaining every time you access the value of the optional.

How do you fix fatal error unexpectedly found nil while unwrapping an optional value?

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.


2 Answers

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.

like image 179
Martin R Avatar answered Sep 24 '22 06:09

Martin R


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.

like image 21
Ratnesh Jain Avatar answered Sep 24 '22 06:09

Ratnesh Jain