Swift's use of Implicitly Unwrapped Optional before availability of nullability

357 views Asked by At

In Apple's blog on nullability, they mention this:

"...in Swift there’s a strong distinction between optional and non-optional references, e.g. NSView vs. NSView?, while Objective-C represents boths of these two types as NSView *. Because the Swift compiler can’t be sure whether a particular NSView * is optional or not, the type is brought into Swift as an implicitly unwrapped optional, NSView!"

Does that mean previously when declaring Objective-C methods as returning implicitly unwrapped optional in Swift, it can in fact crash (since some of the methods declared with implicitly unwrapped optional may return nil)? Or does Apple make sure only those Objective-C methods that absolutely do not return nil get declared as implicitly unwrapped optional?

2

There are 2 answers

9
nhgrif On BEST ANSWER

Apple's frameworks aren't anything special. In the beginning, everything (every object) you used from Objective-C in Swift was an implicitly unwrapped optional. That was because every pointer in Objective-C could possibly return nil.

In fact, even in the era of Objective-C nullability annotations, it's not entirely impossible that a method annotated as nonnull could return nil. Objective-C doesn't enforce the nullability rules, it merely provides a means for annotating your code so that it should be safer to use from Swift. In the case of Apple's frameworks, I'd wager it's a pretty safe bet that you won't have this problem. Or if you do, the next version of Xcode will fix it.

But again, there's nothing special about implicitly unwrapped optionals coming from Objective-C libraries and frameworks. The only thing that an implicitly unwrapped optional tells you is that the framework author has not made the effort to annotate their library yet (you can't leave implicitly unwrapped optionals in an annotated library). And yes, these implicitly unwrapped options can be nil and they can crash your application.

In the case of Apple, if for some reason, you're using say Xcode 7 and Xcode 6 on different projects, if you take something which Xcode 7's update annotations have declared as non-optional, then assuming the implicitly unwrapped optional version in Xcode 6 will never be nil might work out. But if you take something that is an optional in Xcode 7, assuming the implicitly unwrapped version from Xcode 6 will never be nil probably has a decent likelihood of crashing your app.

Ultimately, in Swift, our use of implicitly unwrapped optionals should be few and far between. The primary use of implicitly unwrapped optionals should mostly be reserved for class properties which cannot be set before the class initialization has returned (for example, @IBOutlets in a view controller). Otherwise, they're prone to being the source of numerous "Unexpectedly found nil" questions here on Stack Overflow.


To the question of "Why return an implicitly unwrapped optional rather than an optional?", a few points...

First, that's a language design question. I'm not on the Objective-C or Swift language design team and it's unlikely that anyone from those teams will stop by and answer that question...

Second, that's how the languages are designed to interoperate. Any Objective-C file which has not had nullability annotations added will be treated as if everything is an implicitly unwrapped optional in Swift.

Third, the reason for implicit optionals is it reduces a lot of the verbosity of if let etc statements that optional would require, while not guaranteeing that the variable is actually non-nil. Most of the reason for this is probably because you figure most of these methods actually never return nil.

Fourth, if you know which ones have a chance to be nil and which ones don't, you can actually go ahead and write your Swift code in a way which handles both making assumptions about which way the Objective-C code will be annotated.

For example, with an implicitly unwrapped optional, of course, you can treat it like a non-optional and remove some of the minor verbosity involved with unwrapping.

Additionally, with an implicitly unwrapped optional, if you think it might be nil, all of the optional unwrapping stuff can still work with it.

Example:

override func someFunc(implicitVal: String!) -> String {
    return implicitVal ?? "It was nil!"
}

override func someOtherFunc(implicitVal: String!) -> String {
    return implicitVal
}

If we assumed them all as optionals, the second example wouldn't work

If we assumed them as non-optionals, the first example wouldn't work.

Implicitly unwrapped optionals allows the freedom for the Swift developer to treat them as either if they make the right assumption about the likelihood of the value being nil.

1
newacct On

Does that mean previously when declaring Objective-C methods as returning implicitly unwrapped optional in Swift, it can in fact crash (since some of the methods declared with implicitly unwrapped optional may return nil)?

No. An implicitly-unwrapped optional is an optional -- which means nil is a perfectly acceptable value for it. Having an implicitly-unwrapped optional as nil doesn't crash, only if you try to access a member on it directly without optional-binding or optional-chaining does it crash.

Or does Apple make sure only those Objective-C methods that absolutely do not return nil get declared as implicitly unwrapped optional?

If it were known to not return nil, then Apple, after auditing, would declare it as non-optional, not an implicitly-unwrapped optional. Only if it is not known whether it may return nil or not, does it return implicitly-unwrapped optional.