First, I try mapping a [String?]
, to get a [String]
:
$ xcrun swift
Welcome to Apple Swift version 2.2 (swiftlang-703.0.18.8 clang-703.0.30). Type :help for assistance.
1> import Foundation
2> let j: [String?] = ["a", nil]
j: [String?] = 2 values {
[0] = "a"
[1] = nil
}
3> j.map {$0 ?? ""}
$R0: [String] = 2 values {
[0] = "a"
[1] = ""
}
This makes perfect sense to me. I nil-coalesce a String?
, and I get a String
. But with [AnyObject?]
, something strange occurs:
4> let k: [AnyObject?] = ["a", nil]
k: [AnyObject?] = 2 values {
[0] = "a"
[1] = nil
}
5> k.map {$0 ?? ""}
$R1: [AnyObject?] = 2 values {
[0] = "a"
[1] = (instance_type = 0x00007fff7bc2c140 @"")
}
I'm nil-coalescing optionals, but this time I get out an optional. Why?
The Swift Programming Language says a ?? b
is shorthand for a != nil ? a! : b
, but when I try that, I get out an array of non-optionals:
6> k.map {$0 != nil ? $0! : ""}
$R2: [AnyObject] = 2 values {
[0] = "a"
[1] = ""
}
Am I misunderstanding how ??
is supposed to work? What is going on here?
Discussion. A nil-coalescing operation unwraps the left-hand side if it has a value, or it returns the right-hand side as a default. The result of this operation will have the non-optional type of the left-hand side's Wrapped type.
Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil . If the optional contains a value, the property, method, or subscript call succeeds; if the optional is nil , the property, method, or subscript call returns nil .
In Swift, nil means the absence of a value. Sending a message to nil results in a fatal error. An optional encapsulates this concept. An optional either has a value or it doesn't.
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.
The detailed behaviour is not well-documented, so, would change in the future Swifts.
But you should know coalescing operator has two overloads:
@warn_unused_result
public func ??<T>(optional: T?, @autoclosure defaultValue: () throws -> T) rethrows -> T
@warn_unused_result
public func ??<T>(optional: T?, @autoclosure defaultValue: () throws -> T?) rethrows -> T?
In your case, Swift has chosen the latter for your code.
You can test with a simplified codes like:
let x: AnyObject? = "a"
x ?? ""
The inferred type (in Swift 2.2.1) becomes AnyObject?
.
But this code is also valid.
let y: AnyObject = x ?? ""
String literals like ""
can be treated as variety of types. All of these are valid in Swift.
"" as String
"" as String?
"" as NSString
"" as NSString?
"" as AnyObject
"" as AnyObject?
So, with some unspecified reason Swift has chosen AnyObject?
.
And, in case type inference can be ambiguous, you should use explicit type annotation, as suggested in appzYourLife's comment.
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