So I know the difference between '!' and '?', I just want to know what is the best way to use them when calling a method of an optional variable
var bar: Bar? = nil
bar?.doSomething() // this will be valid, but wouldn't call doSomething
bar!.doSomething() // Given error: EXC_BAD_INSTRUCTIONS (Obviously)
But when 'bar' is not nil, these two method-calls are both valid.
bar = Bar()
bar?.doSomething() // Valid
bar!.doSomething() // Valid
So my question is, what is the best way to call methods of an optional variable, I personally use:
if bar != nil {
bar!.doSomething()
}
or will bar?.doSomething() do exactly the same?
You specify optional chaining by placing a question mark ( ? ) after the optional value on which you wish to call a property, method or subscript if the optional is non- nil . This is very similar to placing an exclamation point ( ! ) after an optional value to force the unwrapping of its value.
Optional Handling. In order to use value of an optional, it needs to be unwrapped. Better way to use optional value is by conditional unwrapping rather than force unwrapping using ! operator.
If you defined a variable as optional, then to get the value from this variable, you will have to unwrap it. This just means putting an exclamation mark at the end of the variable. Optional("Hello, Swift 4!")
I think it is bad advice to avoid bar?.doSomething()
and always use if let bar = bar { ... }
instead.
There are two main reasons:
First, bar?.doSomething()
simply is more concise. If you are writing an if let
with just one statement in the conditional expression then you should probably ask yourself if you don't want to put that code on a single line with the shorter version.
Although this is perfectly legal code ..
if let thing = thing {
thing.bar()
}
.. the following is just as legal, just a lot shorter ..
thing?.bar()
Second, more important and to the core of this issue, they are not the same! The if let
is a combined test, optional unwrapping and assignment while bar?
does something called Optional Chaining.
The latter is super useful in a number of cases that lets you write much nicer code. For example:
It can be used to replace this ..
var imageGenerator: ImageGenerator?
if let generator = imageGenerator {
myView.image = generator.generateImage()
} else {
myView.image = nil
}
.. simply with:
myView.image = self.imageGenerator?.generateImage()
The reason for this is because the Optional Chaining returns nil
as soon as it encounters nil
in the chain. And since UIImageView
has an image: UIImage?
, it takes that nil value. Even if it comes from a ImageGenerator?
It is even more useful when combined with the Nil Coalescing Operator, ??
.
Now you can turn this code ..
var imageGenerator: ImageGenerator?
if let generator = imageGenerator {
myView.image = generator.generateImage()
} else {
myView.image = DefaultImage
}
.. into ..
myView.image = generator?.makeImageGenerator() ?? DefaultImage
.. which is way more concise.
And because Optional Chaining works on Chains .. you can even do something like:
myView.image = delegate?.makeImageGenerator()?.generateImage() ?? DefaultImage
Which would become ..
if let delegate = delegate {
if let generator = delegate.generatorForImage() {
if let image = generator.generateImage() {
myView.image = image
} else {
myView.image = DefaultImage
}
} else {
myView.image = DefaultImage
}
} else {
myView.image = DefaultImage
}
.. if you did not use the Optional Chaining and the Nil Coalescing Operator.
(Yes the myView.image = DefaultImage
could be done only conditionally at the end, but that means you still have to introduce additional code to check if it was set. It wont be much shorter.)
There, two new tricks :-)
When you do
bar?.doSomething()
it is called Optional Chaining: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html
That means, in case bar is nil, it won't continue to doSomething method, but return a nil. If you want to handle cases where it is nil, you should check if an object contains nil or not. You could also do it like this, by optional binding:
if let unwrappedBar = bar {
unwrappedBar.doSomething()
} else {
doSomeOtherThing() // In case it's nil
}
It makes the value available inside the if let statement as a temporary constant called unwrappedBar (you can give it any other name if you want), if it isn't nil.
You can also use a shorter syntax with optional chaining and coalescing operator:
bar?.doSomething() ?? doSomeOtherThing()
Here's more info about different ways to access optional values: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-ID330
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With