swift-可选类型Optional

来源:互联网 发布:笔记本的网络插口坏了 编辑:程序博客网 时间:2024/05/18 02:00

Swift 引入的 Optional,很好的解决了 Objective-C 时代 “nil or not nil” 的问题,配合 Type-Safe 特性,帮我们减少了很多隐藏的问题。下面让我们通过源码来了解一下 Optional 具体的实现逻辑。

初识 Optional

Swift 中的 Optional 想要表达的涵义更多是“有没有”,而非“空不空”,因此在 Swift 中,对 nil 的使用也就不像 Objective-C 中那么随意。我们需要将其声明为 Optional(可能没有),才能将其赋值为 nil(没有),也正因为表达的是“有没有”,所以可选类型也就不局限于对象类型了,基本数据类型同样可以声明 Optional,并赋值为 nil。

基础用法

Swift 声明一个 Optional 变量方法如下:

1
2
3
Swift
 
var optionalValue: Optional

另外,还有一个更常见的声明方式,即 ?:

1
2
Swift
var optionalValue: T?

上面两种方式是等价的,我们得到了一个类型为 T 的可选变量,如果我们不初始化,则可选变量的初始值为 nil,即,该变量没有值。

读取可选变量时,通过 ! 运算符进行强制解包(Forced Unwrapped),不过使用前需要先判断变量是否有值,否则会得到一个 runtime 错误:

1
2
3
4
5
6
7
8
9
10
Swift
var optionalValue_1: OptionaloptionalValue_1 = 5
if optionalValue_1 != nil {
    print(optionalValue_1!) // "5"
else {
    print("optionalValue_1 has no value")
}
var optionalValue_2: Int?
// fatal error: unexpectedly found nil while unwrapping an Optional value
print(optionalValue_2!)

隐式解包(Implicitly Unwrapped)

声明了一个可选变量后,每次读取都需要使用 ! 进行强制解包,写起来比较麻烦,为了简便起见,我们可以将声明中的 ? 替换为 !,这样,使用时系统会对可选变量进行隐式解包,就无需再添加 !:

1
2
3
4
5
6
7
8
Swift
var optionalString: String!
optionalString = "Hello, Implicitly Unwrapped"
if optionalString != nil {
    print(optionalString) // "Hello, Implicitly Unwrapped"
else {
    print("optionalString has no value")
}

如此用起来方便多了,跟 Objective-C 也类似了,可是,这看上去和一个非可选变量的使用完全一样,很容易让人误解为是一个正常变量,一定有值,不需要对其检查就可以使用,这不是有违 Swift 的安全的原则吗?为什么以安全著称的 Swift 会加入隐式解包并允许这种危险的写法呢?王巍(@onevcat)在其《100个Swift开发必备Tip》一书中隐式解包 OPTIONAL章节进行了探讨,以下摘录片段:

一切都是历史的错。因为 Objective-C 中 Cocoa 的所有类型变量都可以指向 nil 的,有一部分 Cocoa 的 API 中在参数或者返回时即使被声明为具体的类型,但是还是有可能在某些特定情况下是 nil,而同时也有另一部分 API 永远不会接收或者返回 nil。在 Objective-C 时,这两种情况并没有被加以区别,因为 Objective-C 里向 nil 发送消息并不会有什么不良影响。在将 Cocoa API 从 Objective-C 转为 Swift 的 module 声明的自动化工具里,是无法判定是否存在 nil 的可能的,因此也无法决定哪些类型应该是实际的类型,而哪些类型应该声明为 Optional。

在这种自动化转换中,最简单粗暴的应对方式是全部转为 Optional,然后让使用者通过 Optional Binding 来判断并使用。虽然这是最安全的方式,但对使用者来说是一件非常麻烦的事情,我猜不会有人喜欢每次用个 API 就在 Optional 和普通类型之间转来转去。这时候,隐式解包的 Optional 就作为一个妥协方案出现了。使用隐式解包 Optional 的最大好处是对于那些我们能确认的 API 来说,我们可直接进行属性访问和方法调用,会很方便。但是需要牢记在心的是,隐式解包不意味着 “这个变量不会是 nil,你可以放心使用” 这种暗示,只能说 Swift 通过这个特性给了我们一种简便但是危险的使用方式罢了。

王巍(@onevcat)隐式解包 OPTIONAL

总之,只要我们在使用时坚持对可选变量进行“为空判断”,即可安全的使用隐式解包特性。

可选绑定(Optional Binding)

可选绑定(Optional Binding)是用来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量,通常用于 if 和 while 语句中:

1
2
3
4
5
6
7
8
Swift
var optionalString: String!
optionalString = "Hello, Optional Binding"
if let tempString = optionalString {
    print(tempString) // "Hello, Optional Binding"
else {
    print("optionalString has no value")
}

Optional 源码解析

在掌握了 Optional 的基本使用方法之后,我们通过 Swift 源码来了解一下 Optional 的实现细节。为了方便理解,我将分定义、解包、运算符和扩展方法 4 个部分进行讲述。

Optional 定义

打开 Optional 源代码(路径:swift/stdlib/public/core/Optional.swift),或通过 Xcode 查看 Optional 定义,可以看到 Optional 其实是一个 enum,具体代码如下(删除注释):

1
2
3
4
5
6
7
8
9
10
11
Optional.swift
public enum Optional : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)
     
    public init(_ some: Wrapped)
    public func map(_ transform: (Wrapped) throws -> U) rethrows -> U?
    public func flatMap(_ transform: (Wrapped) throws -> U?) rethrows -> U?
    public init(nilLiteral: ())
    public var unsafelyUnwrapped: Wrapped { get }
}

通过定义可以得到以下信息:

  • 包含 none 和 some(Wrapped) 两个 case,分别代表可选类型“没有值”和“有值”两种情况

既然是枚举类型,那么我们就可以通过判断 case 是否匹配来读取可选变量的值,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Swift
var optionalValue: String?
// 方式 1
switch optionalValue {
case .none:
    print("optionalString has no value")
default:
    print(optionalValue!)
}
// 方式 2
if optionalValue == .none {
    print("optionalString has no value")
else {
    print(optionalValue!)
}
// 方式 3,推荐
if optionalValue == nil {
    print("optionalString has no value")
else {
    print(optionalValue!)
}

以上三种方式效果等价,不过官方注释里说 In code, the absence of a value is typically written using the `nil` literal rather than the explicit `.none` enumeration case,即“没有值”时推荐使用 nil,而非 .none,因此上述方法 3 为推荐方式,也就是我们通常所用的方式。

  • 包含一个名为 init(_ some: Wrapped) 的初始化方法

init(_ some: Wrapped) 是 Optional 提供的初始化方法,可以通过这个初始化方法来创建一个有初始值的可选变量,因此以下三种方式是等效的:

1
2
3
4
Swift
var optionalValue = Optional(5)
var optionalValue: Optional = 5
var optionalValue: Int? = 5

我们来看看 init(_ some: Wrapped) 的源码:

1
2
3
4
init(_ some: Wrapped)
public init(_ some: Wrapped) {
    self = .some(some)
}

即:将可选变量初始化为 .some(some),这就是一个 wrapped 的值,.some 可以理解为就是 Optional,因此当我们初始化后得到的就是 Optional(some),所谓的解包,其实就是将 some 从 Optional(some) 中解出来:

1
2
3
Swift
var optionalValue = Optional(5)
print(optionalValue) // Optional(5)
  • 包含两个 Map 相关方法

这部分将在本文 Map 章节进行说明。

  • 实现了 protocol ExpressibleByNilLiteral 协议中的 init(nilLiteral: ()) 方法

协议 ExpressibleByNilLiteral 中包括一个 init(nilLiteral: ()) 方法,功能是使用 nil(Optional 中为:.none)进行初始化,源码如下:

1
2
3
4
init(nilLiteral: ())
public init(nilLiteral: ()) {
    self = .none
}

这个方法是不能直接调用的,使用 var i: Index? = nil 的方式会自动调用该方法。

  • 包含一个名为 unsafelyUnwrapped 的只读变量

这部分将在本文 Unwrapped 章节进行说明。

Unwrapped

上面讲到初始化一个可选变量后得到的是一个 wrapped 的值,使用时需要进行 unwrapped,即解包。我们都知道解包只需要在可选变量后面加上 ! 即可。

那么 ! 具体是如何工作的呢?我们从源码(路径:swift/stdlib/public/core/Policy.swift)中找到它的定义:

1
2
3
4
5
Policy.swift
// Optional unwrapping operator is built into the compiler as a part of
// postfix expression grammar.
//
// postfix operator !

原来,解包符号 ! 作为后缀表达式被放入了编译器中,我们无法得到其具体实现,不过这并不妨碍我们理解,因为还有另外一条线索,就是前面提到的名为 unsafelyUnwrapped 的只读变量。

在日常开发中,我们一般不会用到 unsafelyUnwrapped,但其实它与 ! 在一定程度上是等价的,我们先来看看这个变量的源代码:

1
2
3
4
5
6
7
8
9
10
Optional.swift
public var unsafelyUnwrapped: Wrapped {
    @inline(__always)
    get {
        if let x = self {
            return x
        }
        _debugPreconditionFailure("unsafelyUnwrapped of nil optional")
    }
}

unsafelyUnwrapped 的 get 方法和我们常写的可选绑定语法很近似,这看上去非常安全,为什么变量名称是 “unsafely” 呢?稍安勿躁,我们来看看这个变量的官方注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Optional.swift
/// The wrapped value of this instance, unwrapped without checking whether
/// the instance is `nil`.
///
/// The `unsafelyUnwrapped` property provides the same value as the forced
/// unwrap operator (postfix `!`). However, in optimized builds (`-O`), no
/// check is performed to ensure that the current instance actually has a
/// value. Accessing this property in the case of a `nil` value is a serious
/// programming error and could lead to undefined behavior or a runtime
/// error.
///
/// In debug builds (`-Onone`), the `unsafelyUnwrapped` property has the same
/// behavior as using the postfix `!` operator and triggers a runtime error
/// if the instance is `nil`.
///
/// The `unsafelyUnwrapped` property is recommended over calling the
/// `unsafeBitCast(_:)` function because the property is more restrictive
/// and because accessing the property still performs checking in debug
/// builds.
///
/// - Warning: This property trades safety for performance.  Use
///   `unsafelyUnwrapped` only when you are confident that this instance
///   will never be equal to `nil` and only after you've tried using the
///   postfix `!` operator.

大意如下:

unsafelyUnwrapped 就是读取可选变量所包裹的值,不检查是否为 nil;

unsafelyUnwrapped 和 ! 提供相同的值,但在编译优化设置为 (-O) 时,不会做任何检查,因此,直接访问可选变量的 unsafelyUnwrapped 会导致未知结果;

在编译优化设置为 (-Onone) 时,unsafelyUnwrapped 和 ! 表现相同,访问 nil 会引发 runtime 错误;

unsafelyUnwrapped 比调用 unsafeBitCast(_:) 方法更推荐,因为 debug 环境会进行安全检查;

警告:unsafelyUnwrapped 以牺牲安全性来换取更好的性能,因此需要在确保非 nil 的情况下使用。

下面我们分段解读:

对于 1、2、3,来看以下代码:

1
2
3
4
Swift
var optionalValue = Optional(5)
print(optionalValue!) // 5
print(optionalValue.unsafelyUnwrapped) // 5

可选变量有值的情况下,两种解包方式效果相同,再来看下面的代码:

1
2
3
Swift (`-Onone`)
var optionalValue: Optionalprint(optionalValue!) // fatal error: unexpectedly found nil while unwrapping an Optional value
print(optionalValue.unsafelyUnwrapped) // fatal error: unsafelyUnwrapped of nil optional

上述代码是在 (-Onone) 下执行的,两种方式得到不同表述但相同涵义的错误,然后我们将编译优化切换至 (-O):

1
2
3
Swift (`-O`)
var optionalValue: Optionalprint(optionalValue!) // EXC_BAD_INSTRUCTION
print(optionalValue.unsafelyUnwrapped) // 0

这时,使用 ! 解包出现错误,但 unsafelyUnwrapped 却正常输出了 0,而这个 0 对我们来说并不应该出现,因为可能导致程序出现无法预知的错误,这也就是为什么变量名中包含一个 “unsafely” 的原因。

第 4 点中提到了一个名为 unsafeBitCast(_:) 的方法,并说明不推荐使用,为什么呢?我们来看看这个方法的具体源码(路径:swift/stdlib/public/core/Builtin.swift):

1
2
3
4
5
6
7
8
9
10
11
12
13
Builtin.swift
/// Returns the bits of `x`, interpreted as having type `U`.
///
/// - Warning: Breaks the guarantees of Swift's type system; use
///   with extreme care.  There's almost always a better way to do
///   anything.
///
@_transparent
public func unsafeBitCast(_ x: T, to: U.Type) -> U {
  _precondition(MemoryLayout.size == MemoryLayout.size,
    "can't unsafeBitCast between types of different sizes")
  return Builtin.reinterpretCast(x)
}

再来看一个使用的例子:

1
2
3
4
Swift
let array = NSArray(object: "obj")
let str = unsafeBitCast(CFArrayGetValueAtIndex(array, 0), to: CFString.self)
print(str) // obj

可以看出,unsafeBitCast(_:) 会将一个指针指向的内存强制按位转换为目标的类型,并且只进行了简单的 size 判断。这是非常危险的操作,因为这种转换是在 Swift 的类型管理之外进行的,因此编译器无法确保得到的类型是否确实正确,所以使用上需要非常注意。

也是因为上述原因,unsafelyUnwrapped 比 unsafeBitCast(_:) 更安全,因为在 debug 环境下,我们能够在不安全使用的情况下得到错误。

第 5 点警告也是我们日常使用可选变量时应该注意的,无论是使用 ! 或是 unsafelyUnwrapped。

运算符

通过 Xcode 进入 Optional 可以看到很多可选变量的运算符,但除了 ?? 以外均没有注释,下面我们还是通过源码来了解一下吧。

??

?? 是 Swift 中一个非常有用的操作符,用来对 nil 进行快速判断,?? 可以判断输入并在当左侧的值是非 nil 的 Optional 值时返回其值,左侧是 nil 时返回右侧的值。

1
2
3
4
5
6
7
8
Swift
var optionalValue: Int?
var aValue = 5
let result = optionalValue ?? aValue
print(result) // 5
optionalValue = 1
result = optionalValue ?? aValue
print(result) // 1

在 Optional 中有两个 ?? 方法,源码如下(删除注释):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Optional.swift
public func ?? (optional: T?, defaultValue: @autoclosure () throws -> T)
    rethrows -> T {
     
    switch optional {
        case .some(let value):
            return value
        case .none:
            return try defaultValue()
    }
}
public func ?? (optional: T?, defaultValue: @autoclosure () throws -> T?)
    rethrows -> T? {
     
    switch optional {
        case .some(let value):
            return value
        case .none:
            return try defaultValue()
    }
}

我们可以得到以下信息:

两个方法的实现完全一样,只是第二个参数允许接收 T 和 T? 两种类型;

?? 右侧的参数 defaultValue 被封装为了 () -> T 和 () -> T?。为什么这样做呢?下面再次摘录王巍(@onevcat)在其《100个Swift开发必备Tip》一书中@AUTOCLOSURE 和 ??章节部分片段进行解释:

可能你会有疑问,为什么这里要使用 autoclosure,直接接受 T 作为参数并返回不行么,为何要用 () -> T 这样的形式包装一遍,岂不是画蛇添足?其实这正是 autoclosure 的一个最值得称赞的地方。如果我们直接使用 T,那么就意味着在 ?? 操作符真正取值之前,我们就必须准备好一个默认值传入到这个方法中,一般来说这不会有很大问题,但是如果这个默认值是通过一系列复杂计算得到的话,可能会成为浪费 – 因为其实如果 optional 不是 nil 的话,我们实际上是完全没有用到这个默认值,而会直接返回 optional 解包后的值的。这样的开销是完全可以避免的,方法就是将默认值的计算推迟到 optional 判定为 nil 之后。

王巍(@onevcat)@AUTOCLOSURE 和 ??

 

我们通过源码也可以看出,当满足 case .none 时,直接返回 defaultValue(),正是因为这一巧妙的用法,克服了性能上的开销和使用上的不便,以如此优雅的方式返回默认值。

==

在 Optional 中存在三个 == 运算符的实现,用于对比可选变量的相等关系,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Optional.swift
public func == (lhs: T?, rhs: T?) -> Bool {
    switch (lhs, rhs) {
        case let (l?, r?):
            return l == r
        case (nil, nil):
            return true
        default:
            return false
    }
}
public func == (lhs: T?, rhs: _OptionalNilComparisonType) -> Bool {
    switch lhs {
        case .some(_):
            return false
        case .none:
            return true
    }
}
public func == (lhs: _OptionalNilComparisonType, rhs: T?) -> Bool {
    switch rhs {
        case .some(_):
            return false
        case .none:
            return true
    }
}

第一个方法中,== 被用来对比两个遵循 protocol Equatable 的可选变量的等价关系,通过源码可以看出,以下两种情况会返回 true:

两个可选变量均为 nil;

两个可选变量均非 nil,且所包裹的值相等。

不满足以上条件的情况均返回 false,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
Swift
let group1 = [1, 2, 3, 4, 5]
let group2 = [1, 3, 5, 7, 9]
if group1.first == group2.first {
    print("Start the same."// Start the same.
}
let left: Int? = nil
let right: Int? = nil
if left == right {
print("Equal!"// Equal!
}

另外,我们还可以将可选变量与非可选变量或常量进行比较,系统会将非可选变量或常量先 “包裹” 成一个 Optional 变量再进行上述比较:

1
2
3
4
5
6
7
8
9
Swift
let left: Int = 5
let right: Int? = 5
if left == right {
    print("Equal!"// Equal!
}
if 5 == right {
    print("Equal Too!"// Equal Too!
}

第二个和第三个方法我们也经常用到,它允许我们将可选变量与 nil 进行直接比较,与第一个方法的区别在于这里的可选变量即使不遵循 protocol Equatable 也可以进行比较,方法实现与判断可选变量是否有值的方式很类似。

参数中 _OptionalNilComparisonType 的定义源码如下:

1
2
3
4
5
6
7
Optional.swift
public struct _OptionalNilComparisonType : ExpressibleByNilLiteral {
    /// Create an instance initialized with `nil`.
    @_transparent
    public init(nilLiteral: ()) {
    }
}

使用方法如下:

1
2
3
4
5
Swift
var str: Data? = nil
if str == nil {
    print("Data is nil."// Data is nil.
}

!=

!= 用于可选变量的 “不等于” 判断,实现方法共有三个,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Optional.swift
public func != (lhs: T?, rhs: T?) -> Bool {
    return !(lhs == rhs)
}
public func != (lhs: T?, rhs: _OptionalNilComparisonType) -> Bool {
    switch lhs {
        case .some(_):
            return true
        case .none:
            return false
    }
}
public func != (lhs: _OptionalNilComparisonType, rhs: T?) -> Bool {
    switch rhs {
        case .some(_):
            return true
        case .none:
            return false
    }
}

有了 == 的经验,!= 就很好理解了,只是对 == 进行逻辑取反,这里不再进行赘述。

~=

在 Optional 中,还有一个 ~= 运算符,源码为:

1
2
3
4
5
6
7
8
9
Optional.swift
public func ~= (lhs: _OptionalNilComparisonType, rhs: T?) -> Bool {
    switch rhs {
        case .some(_):
            return false
        case .none:
            return true
    }
}

奇怪,这个方法的实现与 == 中第三个方法相同,为什么要多加一个 ~= 呢?

~= 被称为模式匹配运算符(Pattern Matching),在可选变量的判断上与 == 确实有相同的功效,但是在使用上官方给出如下解释:

1
2
3
4
5
Optional.swift
/// - Note: To test whether an instance is `nil` in an `if` statement, use the
///   equal-to operator (`==`) instead of the pattern-matching operator. The
///   pattern-matching operator is primarily intended to enable `case`
///   statement pattern matching.

原来,~= 主要是被用于 case 语句中的模式匹配,正常的可选变量判断,我们仍然需要使用 ==。

Optional Extensions

在 Optional 中,还存在两个 extension,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Optional.swift
extension Optional : CustomDebugStringConvertible {
    /// A textual representation of this instance, suitable for debugging.
    public var debugDescription: String {
        switch self {
            case .some(let value):
                var result = "Optional("
                    debugPrint(value, terminator: "", to: &result)
                    result += ")"
                return result
            case .none:
                return "nil"
        }
    }
}
extension Optional : CustomReflectable {
    public var customMirror: Mirror {
        switch self {
            case .some(let value):
                return Mirror(
                    self,
                    children: [ "some": value ],
                    displayStyle: .optional)
            case .none:
                return Mirror(self, children: [:], displayStyle: .optional)
        }
    }
}

debugDescription 变量输出其实就是我们常常看到的 Optional(some),主要用于 debug:

1
2
3
Swift
var optionalValue: Int? = 5
print(optionalValue.debugDescription) // Optional(5)

第二个 extension 中的 customMirror 其实是创建了一个当前可选变量的 Mirror,Mirror 是 Swift 中定义的一个用于表示子结构的结构体类型(可以参考:Mirror API Reference),这里不再展开,后续有时间我们再详细论述。

Optional 补充

感谢您坚持阅读到这里,经过上述的讨论,我们已经对 Optional 有了很详细的了解,不过还有两个相对比较重要的概念需要进行说明,就是可选链(Optional Chaining)和 Map,由于篇幅所限,以下仅进行简单的讨论,详细内容大家可以查阅官方文档或是文末提供的参考资料。

可选链(Optional Chaining)

对于可选链,官方文档给出了如下定义:

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. Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.

简而言之,就是当我们需要通过一条链(链上各个环节都可能返回 nil)访问某个属性、方法和下标时,可以通过可选链的方式代替强制解包,以此简化代码。

以官方代码为例:

1
2
3
4
5
6
7
8
9
10
11
Swift
class Person {
    var residence: Residence?
}
  
class Residence {
    var numberOfRooms = 1
}
let john = Person()
let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error

由于访问链路径上 residence 为 nil,因此访问出错,这时,我们可以通过可选链方式避免这样的错误:

Swift

1
2
3
4
5
6
if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
else {
    print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."

通过上述方式,告知 Swift 帮我们在 residence 有值时解析出 numberOfRooms,从而避免访问出错,或是写繁琐的判断代码。

Map

Optional 中包括两个 Map 相关方法,其源代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Optional.swift
public func map(_ transform: (Wrapped) throws -> U)
    rethrows -> U? {
    switch self {
        case .some(let y):
            return .some(try transform(y))
        case .none:
            return .none
    }
}
public func flatMap(_ transform: (Wrapped) throws -> U?)
    rethrows -> U? {
    switch self {
        case .some(let y):
            return try transform(y)
        case .none:
            return .none
    }
}

Optional Map 提供了一种便捷的对可选变量进行变换(transform)的方法,使用 map 方法时,传入一个 transform 闭包,内部可以将接收到可选变量解包后的值,然后在闭包内完成“变换”,再返回。如下例:计算一个可选变量的平方:

1
2
3
4
5
6
7
8
9
Swift
// Sample 1
let possibleNumber: Int? = Int("42")
let possibleSquare = possibleNumber.map { $0 * $0 }
print(possibleSquare) // Optional(1746)
// Sample 2
let noNumber: Int? = nil
let noSquare = noNumber.map { $0 * $0 }
print(noSquare) // nil

Sample 1 闭包中的 $0 即可选变量解包后的值(42,非 Optional(42))。如果可选变量没有值,那么返回即为 nil,如 Sample 2。

那么 flatMap 方法呢?

从声明上看,flatMap 返回的是 U?,说明该方法可以返回 Optional;从源码上看,flatMap 与 map 的区别在于满足 case .some(let y) 时的返回值的不同,map 要求返回 .some(try transform(y)),说明必须是 Optional “包裹”的值类型,其实就是非 Optional 类型,而 flatMap 返回 transform(y),其实是将返回值类型交给 transform() 决定,Optional 或非 Optional 均可。

flatMap 测试例子如下:

1
2
3
4
5
6
Swift
let optionalArray: [String?] = ["A""B""C"];
var optionalResult = optionalArray.flatMap{ $0 }
print(optionalResult) // ["A", "B", "C"]
optionalResult = optionalArray.map{ $0 }
print(optionalResult) // error: cannot convert value of type 'String?' to closure result type 'String'

参考资料

原创粉丝点击