(备注:答案已更新,以反映 Swift 3 及更高版本中的语法变化,例如废除了ImplicitlyUnwrappedOptional。)
Optional.map() 和 Optional.flatMap() 声明如下(我省略了与此处无关的 throws/rethrows 修饰符):
func map<U>(_ transform: (Wrapped) -> U) -> U?
func flatMap<U>(_ transform: (Wrapped) -> U?) -> U?
让我们考虑使用“map”的第一个示例的简化版本:
let number: Int? = 1
let res1 = number.map { $0 + 1 }
print(res1) // Optional(2)
number 的类型为Int?,闭包类型推断为(Int) -> Int。 U为Int,返回值的类型为Int?。 number 不是nil,所以它被解包并通过1 被传递给闭包。闭包返回2 和map 返回Optional(2)。如果number 是nil,那么结果将是nil。
现在我们考虑使用“flatMap”的第二个示例的简化版本:
let number: Int? = 1
let res2 = number.flatMap { $0 + 1 }
print(res2) // Optional(2)
flatMap 需要 (Wrapped) -> U? 类型的闭包,但 { $0 + 1 } 不返回可选值。为了使其编译,编译器将其转换为
let res2 = number.flatMap { return Optional($0 + 1) }
现在闭包的类型为(Int) -> Int?,而U 又是Int。同样,number 被解包并传递给闭包。闭包返回Optional(2),这也是flatMap的返回值。如果number 是nil 或,如果闭包将返回nil,那么结果将是nil。
所以这些调用之间确实没有区别:
let res1 = number.map { $0 + 1 }
let res2 = number.flatMap { $0 + 1 }
但这不是flatMap 的用途。一个更现实的例子是
func foo(_ s : String?) -> Int? {
return s.flatMap { Int($0) }
}
print(foo("1")) // Optional(1)
print(foo("x")) // nil (because `Int($0)` returns nil)
print(foo(nil)) // nil (because the argument is nil)
通常,map 采用 (Wrapped) -> U 类型的闭包并进行转换
Optional<Wrapped>.none --> Optional<U>.none
Optional<Wrapped>.some(wrapped) --> Optional<U>.some(transform(wrapped))
flatMap 采用 (Wrapped) -> U? 类型的闭包并转换
Optional<Wrapped>.none --> Optional<U>.none
Optional<Wrapped>.some(wrapped) --> transform(wrapped)
这里的transform(wrapped) 也可以是Optional<U>.none。
如果(如您的示例中)flatMap 被调用时使用了一个 not 返回可选项的闭包,则编译器会自动将其转换为可选项,并且与 map 没有区别没有了。