Given the following in Swift:
var optionalString: String?
let dict = NSDictionary()
What is the practical difference between the following
They are two different forms of Downcasting in Swift.
(as?
), which is know to be the Conditional Form, returns an optional value of the type you are trying to downcast to.
You can use it when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.
(as!
), which is know to be the Forced Form, attempts the downcast and force-unwraps the result as a single compound action.
You should use it ONLY when you are sure that the downcast will always succeed. This form of the operator will trigger a runtime error if you try to downcast to an incorrect class type.
For more details, please check Type Casting section of Apple's documentation.
I am novice to Swift and writing this example trying to explain as i understand about 'optionals'. If i am wrong please correct me.
Thanks.
class Optional {
var lName:AnyObject! = "1"
var lastName:String!
}
let obj = Optional()
print(obj.lName)
print(obj.lName!)
obj.lastName = obj.lName as? String
print(obj.lastName)
(1) : obj.lastName = obj.lName as! String
vs
(2) : obj.lastName = obj.lName as? String
Ans : (1) Here programmer is damm sure that “obj.lName”
contains string type object. So just give that value to “obj.lastName”
.
Now, if programmer is correct means "obj.lName"
is string type object, then no problem. "obj.lastName" will set to the same value.
But if programmer is wrong means "obj.lName"
is not string type object i.e. it contains some other type object like "NSNumber" etc. Then CRASH (Run Time Error).
(2) Programmer is not sure that “obj.lName”
contains string type object or any other type object. So set that value to “obj.lastName”
if it is string type.
Now, if programmer is correct means “obj.lName”
is string type object, then no problem. “obj.lastName”
will set to the same value.
But if programmer is wrong means obj.lName is not string type object i.e. it contains some other type object like "NSNumber"
etc. Then “obj.lastName”
will set to the nil value. So, No Crash (Happy:)
The practical difference is this:
var optionalString = dict["SomeKey"] as? String
optionalString
will be a variable of type String?
. If the underlying type is something other than a String
this will harmlessly just assign nil
to the optional.
var optionalString = dict["SomeKey"] as! String?
This says, I know this thing is a String?
. This too will result in optionalString
being of type String?
, but it will crash if the underlying type is something else.
The first style is then used with if let
to safely unwrap the optional:
if let string = dict["SomeKey"] as? String {
// If I get here, I know that "SomeKey" is a valid key in the dictionary, I correctly
// identified the type as String, and the value is now unwrapped and ready to use. In
// this case "string" has the type "String".
print(string)
}
It may be easiest to remember the pattern for these operators in Swift as: !
implies "this might trap," while ?
indicates "this might be nil."
refer to: https://developer.apple.com/swift/blog/?id=23
Maybe this code example will help someone grok the principle:
var dict = [Int:Any]()
dict[1] = 15
let x = dict[1] as? String
print(x) // nil because dict[1] is an Int
dict[2] = "Yo"
let z = dict[2] as! String?
print(z) // optional("Yo")
let zz = dict[1] as! String // crashes because a forced downcast fails
let m = dict[3] as! String?
print(m) // nil. the forced downcast succeeds, but dict[3] has no value
as? Types
- means the down casting process is optional. The process can be successful or not(system will return nil if down casting fails).Any way will not crash if down casting fails.
as! Type?
- Here the process of down casting should be successful (!
indicates that) . The ending question mark indicates whether final result can be nil or not.
More info regarding "!" and "?"
Let us take 2 cases
Consider:
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell
Here we don't know whether the result of down casting of cell with identifier "Cell" to UITableViewCell is success or not. If unsuccessful then it returns nil( so we avoid crash here). Here we can do as given below.
if let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell {
// If we reached here it means the down casting was successful
}
else {
// unsuccessful down casting
}
So let us remember it like this - If ?
it means we are not sure whether value is nil or not (question mark comes when we don't know things).
Contrast that to:
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell.
Here we tell the compiler that down casting should be successful. If it fails the system will crash. So we give !
when we are sure that value is non nil.