为什么 Swift nil-coalescing 返回一个 Optional?
Why is Swift nil-coalescing returning an Optional?
首先,我尝试映射 [String?]
,得到 [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 {[=11=] ?? ""}
$R0: [String] = 2 values {
[0] = "a"
[1] = ""
}
这对我来说非常有意义。我没有合并 String?
,得到 String
。但是对于 [AnyObject?]
,会发生一些奇怪的事情:
4> let k: [AnyObject?] = ["a", nil]
k: [AnyObject?] = 2 values {
[0] = "a"
[1] = nil
}
5> k.map {[=12=] ?? ""}
$R1: [AnyObject?] = 2 values {
[0] = "a"
[1] = (instance_type = 0x00007fff7bc2c140 @"")
}
我没有合并可选项,但这次我得到了一个可选项。为什么?
Swift Programming Language 说 a ?? b
对于 a != nil ? a! : b
是 shorthand,但是当我尝试这样做时,我得到了一个非可选数组:
6> k.map {[=13=] != nil ? [=13=]! : ""}
$R2: [AnyObject] = 2 values {
[0] = "a"
[1] = ""
}
我是不是误解了 ??
的工作原理?这是怎么回事?
详细的行为没有很好的记录,因此,将来会改变 Swifts。
但是你应该知道合并运算符有两个重载:
@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?
在您的情况下,Swift 为您的代码选择了后者。
您可以使用以下简化代码进行测试:
let x: AnyObject? = "a"
x ?? ""
推断类型(在 Swift 2.2.1 中)变为 AnyObject?
。
但是这段代码也是有效的。
let y: AnyObject = x ?? ""
像 ""
这样的字符串文字可以被视为多种类型。所有这些都在 Swift.
中有效
"" as String
"" as String?
"" as NSString
"" as NSString?
"" as AnyObject
"" as AnyObject?
因此,由于某些未指明的原因,Swift 选择了 AnyObject?
。
而且,如果类型推断可能不明确,您应该使用显式类型注释,如 appzYourLife 的评论中所建议的那样。
我注意到 Apple 认为这是 Swift 2.
中的错误
在 Swift3 中,上面的第一个例子仍然有效,而第二个和第三个例子是无效的语法(有或没有 Foundation 桥接)。
将 AnyObject
声明替换为 Any
有效:a ?? b
的行为与 a != nil ? a! : b
相同,如文档所述。
首先,我尝试映射 [String?]
,得到 [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 {[=11=] ?? ""}
$R0: [String] = 2 values {
[0] = "a"
[1] = ""
}
这对我来说非常有意义。我没有合并 String?
,得到 String
。但是对于 [AnyObject?]
,会发生一些奇怪的事情:
4> let k: [AnyObject?] = ["a", nil]
k: [AnyObject?] = 2 values {
[0] = "a"
[1] = nil
}
5> k.map {[=12=] ?? ""}
$R1: [AnyObject?] = 2 values {
[0] = "a"
[1] = (instance_type = 0x00007fff7bc2c140 @"")
}
我没有合并可选项,但这次我得到了一个可选项。为什么?
Swift Programming Language 说 a ?? b
对于 a != nil ? a! : b
是 shorthand,但是当我尝试这样做时,我得到了一个非可选数组:
6> k.map {[=13=] != nil ? [=13=]! : ""}
$R2: [AnyObject] = 2 values {
[0] = "a"
[1] = ""
}
我是不是误解了 ??
的工作原理?这是怎么回事?
详细的行为没有很好的记录,因此,将来会改变 Swifts。
但是你应该知道合并运算符有两个重载:
@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?
在您的情况下,Swift 为您的代码选择了后者。
您可以使用以下简化代码进行测试:
let x: AnyObject? = "a"
x ?? ""
推断类型(在 Swift 2.2.1 中)变为 AnyObject?
。
但是这段代码也是有效的。
let y: AnyObject = x ?? ""
像 ""
这样的字符串文字可以被视为多种类型。所有这些都在 Swift.
"" as String
"" as String?
"" as NSString
"" as NSString?
"" as AnyObject
"" as AnyObject?
因此,由于某些未指明的原因,Swift 选择了 AnyObject?
。
而且,如果类型推断可能不明确,您应该使用显式类型注释,如 appzYourLife 的评论中所建议的那样。
我注意到 Apple 认为这是 Swift 2.
中的错误在 Swift3 中,上面的第一个例子仍然有效,而第二个和第三个例子是无效的语法(有或没有 Foundation 桥接)。
将 AnyObject
声明替换为 Any
有效:a ?? b
的行为与 a != nil ? a! : b
相同,如文档所述。