Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use (?) and (!) in Swift

I'm new to swift and I'm having some difficulties understanding on how to use (!) and (?)

As far as I know, we can use (?) when there are instances that a variable can be nil. And use (!) when you are 100% sure that item is not nil.

1. Working Fine - Optionals

var john:String?
john = "Is my name"
println(john!)

2. Crashes on Runtime - ! must not be nil - means this is correct

var john:String?
println(john!)

3. Works Fine

var dict: [String:AnyObject] = Dictionary()
dict["name"] = "John"
var str: String = dict["name"]! as String <--- Taking away the (!) says it's not convertible to String

4. Cannot Run/Build - for me it's similar to 1.

var dict: [String:AnyObject]? = Dictionary() ---error says does not have a member named 'subscript'
dict["name"] = "John"
var str: String = dict["name"]! as String

5. Unexpectedly found nil while unwrapping an optional value

var dict: [String:AnyObject] = Dictionary()
dict["name"]? = "John"
var str: String = dict["name"]! as String

Would be great if someone can help me understand these things. Thanks!

like image 537
Shlby Puerto Avatar asked Sep 03 '14 08:09

Shlby Puerto


1 Answers

it is a bit misleading interpretation believing when an ! 'marks' an ivar then that 100% cannot be nil. it can be. you can say only, you got the value as already unwrapped, so you don't need to force unwrapping it again – but it can be nil.

try this example for instance:

var text: String! = "hello"
text = nil;
println(text)

it prints a nil for you.

the reason why your app can crash is you force unwrapping an optional which is nil, that is invalid operand.


#4

line-by-line:

var dict: [String:AnyObject]? = Dictionary() // from OP

your dict is an optional, let us see what you are doing here:

dict["name"] = "John" // from OP
var str: String = dict["name"]! as String // from OP

you have an optional dict and you'd like to use it somehow, you have two possible ways to do it:

  • (A) via optional chaining;
  • (B) via forced unwrapping;

(A)

dict?["name"] = "John" // optional chaining

it is quite straightforward, it assigns the new value for the key name if the dictionary is not nil, otherwise the chain generously falls and nothing happens in runtime.

in perspective of this line:

var str: String = dict!["name"]! as String // forcibly unwrapped

it crashes in runtime if either the dictionary or the value for the key was nil (as per the first paragraph says: invalid operand to force unwrapping a nil), but the str would be John if the dictionary and the key both do valid objects.

(B)

dict!["name"] = "John" // forcibly unwrapped

it works like a charm and assigns the new value for the key name if the dict exists; but if the dict was nil, that is a termination point in runtime (aka crash), because nil cannot be unwrapped forcibly (see above).


#5

line-by-line:

var dict: [String:AnyObject] = Dictionary() // from OP

your dict is not optional and not even nil, but the dictionary is literally empty, so no key does exist in it, including the name.

dict["name"]? = "John" // from OP
var str: String = dict["name"]! as String // from OP

the optional chaining always falls when any of the element of the chain falls – therefore no new value will be assigned in your code, but the falling happens gracefully, so you bypass the first line about assigning the new value, but the app crashes in the second line because the value does not exists and you try to force unwrapping it (see above about invalid operand).

so, you need to drop the optional chaining from the first line, if you want to assign a new value for a non-existing key:

dict["name"] = "John"

the optional chaining is useful if you would not like to change the original dictionary with adding a new key/value, but you would like to override an existing one only:

dict["name"] = "John"
dict["name"]? = "Jack"

in that case the new value will be Jack, because the optional chaining won't fall as the key name is already existing with a different value, so it can be and will be overridden; but:

dict["name"] = nil
dict["name"]? = "Jack"

the optional chaining will falls and no new value is assigned here for the key.


NOTE: there would be many other things and ideas which can be told about the concept. the original documentation is available on Apple site under section Swift Resources.

like image 95
holex Avatar answered Sep 30 '22 20:09

holex