jonthornham
jonthornham

Reputation: 3335

About Optionals in Swift

I am working on a new project in Swift and am having a tough time understand a particular use of optionals. I have declared a UIRefreshControl property for use in the class. I declared it as an optional.

var refreshControl : UIRefreshControl?

In the viewDidLoad() I first tried the following.

override func viewDidLoad()
    {
        super.viewDidLoad()

        self.refreshControl!.addTarget(self, action: Selector("refreshInvoked:"), forControlEvents: UIControlEvents.ValueChanged)
        self.feedsTableView.addSubview(self.refreshControl!)
    }

The app crashed on

self.refreshControl!.addTarget(self, action: Selector("refreshInvoked:"), forControlEvents: UIControlEvents.ValueChanged 

telling me the following. "fatal error: unexpectedly found nil while unwrapping an Optional value."

I realized that I had not instantiated the UIRefreshControl object so I tried the following to fix it.

override func viewDidLoad()
    {
        super.viewDidLoad()

        self.refreshControl! = UIRefreshControl()
        self.refreshControl!.addTarget(self, action: Selector("refreshInvoked:"), forControlEvents: UIControlEvents.ValueChanged)
        self.feedsTableView.addSubview(self.refreshControl!)
    }

To my surprise I received the same error message for

self.refreshControl! = UIRefreshControl()

When I remove the ! the error goes away and everything works.

self.refreshControl = UIRefreshControl()

Questions

  1. Why don't we have to forcibly unwrap the optional when we instantiate it?

  2. Would I be better off declaring the property as an implicitly unwrapped optional? If so why?

    var refreshControl : UIRefreshControl!

Upvotes: 2

Views: 219

Answers (2)

Krunal
Krunal

Reputation: 79636

Well...

? (Optional) indicates your variable may contain a nil value while ! (unwrapper) indicates your variable must have a memory (or value) when it is used (tried to get a value from it) at runtime.

The main difference is that optional chaining fails gracefully when the optional is nil, whereas forced unwrapping triggers a runtime error when the optional is nil.

To reflect the fact that optional chaining can be called on a nil value, the result of an optional chaining call is always an optional value, even if the property, method, or subscript you are querying returns a nonoptional value. You can use this optional return value to check whether the optional chaining call was successful (the returned optional contains a value), or did not succeed due to a nil value in the chain (the returned optional value is nil).

Specifically, the result of an optional chaining call is of the same type as the expected return value, but wrapped in an optional. A property that normally returns an Int will return an Int? when accessed through optional chaining.

var defaultNil : Int?  // declared variable with default nil value
println(defaultNil) >> nil  

var canBeNil : Int? = 4
println(canBeNil) >> optional(4)

canBeNil = nil
println(canBeNil) >> nil

println(canBeNil!) >> // Here nil optional variable is being unwrapped using ! mark (symbol), that will show runtime error. Because a nil optional is being tried to get value using unwrapper

var canNotBeNil : Int! = 4
print(canNotBeNil) >> 4

var cantBeNil : Int = 4
cantBeNil = nil // can't do this as it's not optional and show a compile time error

Here is basic tutorial in detail, by Apple Developer Committee.

Upvotes: 0

Antonio
Antonio

Reputation: 72750

  1. Unwrapping is an action that is done on an optional variable to extract the value stored in it, if not nil. The opposite action is wrapping, done when a value is stored in an optional variable - which doesn't need any special operator to be performed. If you use forced unwrapping to assign, you are actually unwrapping a value from an optional variable, which is nil, causing the exception to be thrown

  2. It makes sense declaring a property as implicitly unwrapped if these conditions are met:

    a. the property is supposed to be non-nil for the entire lifecycle of the container instance

    b. the property cannot be initialized in the constructor

    This is the case of all outlets, which are defined as implicitly unwrapped because their initialization is done at a later stage in the view controller life cycle. I usually avoid implicitly unwrapped optionals, that's one of the few cases where I tolerate them.

Upvotes: 3

Related Questions