Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do Objective-C APIs return implicitly unwrapped optionals?

I am rather perplexed by this. If we take the method cellForRowAtIndexPath: in UITableView for example, it's method signature is:

func cellForRowAtIndexPath(_ indexPath: NSIndexPath!) -> UITableViewCell!

And its return value is:

An object representing a cell of the table or nil if the cell is not visible or indexPath is out of range.

That sounds like the perfect reason to use a standard optional. In fact, since all pointer based types in Objective-C can be nil... it seems to make sense that all Objective-C pointer types should be imported as standard optionals.

I know from the WWDC talk that they say that for implicitly unwrapped optionals:

  • Can be tested explicitly for nil
  • Can directly access properties/methods of the underlying value
  • Can be implicitly converted to its underlying value

And from Apple's Using Swift with Cocoa and Objective-C:

When you access the value in this kind of optional type without safely unwrapping it first, the implicitly unwrapped optional checks whether the value is missing. If the value is missing, a runtime error occurs.

So, instead of importing a possibly nil value into Swift as an optional, they decided to import it as something that states that this should never be nil... but could be? It sounds like they completely negated the safety of the optional type in Swift for Objective-C APIs by doing this. What do I seem to be missing?

Instead of giving a compile time error or warning, they decided a runtime error was better? This is very confusing.

Considering that nothing seems to answer this question that I have seen... I am thinking it is something obvious to everybody else that I am just not seeing but... Why is it like this?

Is it really just to save people from using if let or optional chaining when they use Objective-C APIs in Swift, or something more?

like image 304
NickH Avatar asked Jul 14 '14 00:07

NickH


People also ask

Why are outlets defined as implicitly unwrapped optionals?

It's only after the view controller is initialized that it loads its view. This also means that any outlets declared in the view controller class don't have a value immediately after the view controller's initialization. That's why an outlet is always declared as an (implicitly unwrapped) optional.

What are implicitly unwrapped optionals?

Checking an optionals value is called “unwrapping”, because we're looking inside the optional box to see what it contains. Implicitly unwrapping that optional means that it's still optional and might be nil, but Swift eliminates the need for unwrapping.

What is if let Swift?

The “if let” allows us to unwrap optional values safely only when there is a value, and if not, the code block will not run. Simply put, its focus is on the “true” condition when a value exists.


2 Answers

When you make an implicitly unwrapped optional in Swift, it does not mean that it is always going to be non-nil: all it means is that you tell the compiler that when you access their properties, you expect the object to be non-nil. The object that you reference can be explicitly checked for nil; setting it to nil will not cause an exception either, unless you try to access any of its properties after that.

When Apple used implicitly unwrapped optionals for the parameters of

func tableView(_ tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!

function, they let you save on a few extra if - let. In this case, they know that they never pass you a nil; in other cases, they do not know it, and they expect you to nil-check the object.

They allow you to return nil, too. It is up to them to check the results for nil, unless, of course, you decide to call that function yourself. Although I cannot think of a valid reason to call cellForRowAtIndexPath from your own code, if you do make a call, it would be your responsibility to check the return value for nil.

If you consider an alternative of making the parameters UITableView? and NSIndexPath? instead, all implementations would have to either use an exclamation point after tableView and indexPath, or use the if - let idiom. Compared to this choice, implicitly unwrapped types look like a better choice.

like image 105
Sergey Kalinichenko Avatar answered Sep 29 '22 07:09

Sergey Kalinichenko


The following was Greg Parker's answer in swift-users at lists.swift.org:

Importing as implicitly-unwrapped optional is a usability compromise. Most Objective-C pointers are never actually nil. If a pointer is nil, and the author didn't check, then the process deliberately halts. This is no worse than the behavior you get when writing Objective-C code. IUO import is intended to be a stopgap. In the long term every Objective-C interface ought to be explicitly annotated so that Swift can import them more precisely. In your own code you can use NS_ASSUME_NONNULL_BEGIN/END in your header files. Every un-annotated object pointer inside those markers is nonnull.

like image 26
Jenus Dong Avatar answered Sep 29 '22 09:09

Jenus Dong