Swift 4.2 新特性详解 CaseIterable.allCases

Swift 4.2 在 WWDC 18 上正式公布,作为 Xcode 10 搭载的 Swift 最新版,以及 Swift 5 到来之前的重要一步,它有哪些新特性呢?首先我们来了解一下CaseIterable


Swift 4.2 新特性详解 CaseIterable.allCases_第1张图片
Xcode 10 中的 Swift 4.2

合成枚举 allCases

Swift 4.2 引入一个新的 protocol CaseIterable,它被用于合成简单枚举类型的 allCases 静态属性,代码如下:

enum Weekday : String, CaseIterable {
    case monday, tuesday, wednesday, thursday, friday
}

print(Weekday.allCases)

所谓“简单枚举类型”,指的是不带关联值的枚举类型。所以,如果上述 Weekday 枚举声明成 enum Weekday : String, CaseIterable 的话(指定rawType),编译器也是能够自动合成的。

Swift 支持一些标准库类型的自动合成,在Swift 4.1 中,我们曾就提到过 合成的Equatable 和 Hashable以及 Codable。所以 CaseIterable 是另一个支持自动合成的 -able 协议。

然而,自动合成特性尽管方便,但是也会造成记忆成本,大家需要记住什么时候会合成而什么时候不会。另一方面,因为方便,那么对实际概念可能不求胜解。例如,Codable的改进 中的 CodingKey 由于编译器支持自动合成,那么大家对 CodingKey 的实际理解可能就会流于声明的表象。

自实现 CaseIterable

当无法自动合成的时候,我们可以自己实现 CaseIterable。例如,带关联值的枚举:

enum MartialStatus : CaseIterable {
    case single
    case married(spouse: String)
    
    static var allCases: [MartialStatus] {
        return [.single, .married(spouse: "Leon")]
    }
}

再比如,某个case在某种情况下不可用,或者默认合成的实现不是你想要的时候,也可以自己实现。

enum MartialStatus : CaseIterable {
    @available(*, unavailable)
    case single
    case married

    static var allCases: [MartialStatus] { return [.married]}
}

print(MartialStatus.allCases)

也许你会好奇,既然某些情况下编译器不能合成,那么我们完全可以自己添加 allCases 属性,而不声明实现 CaseIterable。首先,这样是可以的,因为以前就是这么做的。其次,你也有可能会失去一些其他方面针对 CaseIterable 协议设计的一些功能;当然到需要的时候,再通过extension来扩展声明实现也是可以的。

详解 CaseIterable

CaseIterable 如同 Equatable 和 Hashable 一样,是泛型约束,而不是泛型类型,它的具体定义如下:

public protocol CaseIterable {

    /// A type that can represent a collection of all values of this type.
    associatedtype AllCases : Collection where Self.AllCases.Element == Self

    /// A collection of all values of this type.
    static var allCases: Self.AllCases { get }
}

首先,它定义类一个关联类型 AllCases 作为 allCases 的返回值,然后约束这个类型是一个 Collection,并且 Element 和 这个枚举一致。我们之前的例子使用了Array,因此满足这个约束。

当然,这个 CaseIterable 也可以简单些,比如这样:

public protocol CaseIterable2 {    
    static var allCases: [Self] { get }
}

这当然也是可以的,缺点是没有那么面向协议了。标准库之所以采用了约束的形式,是希望不跟任何具体 Collection 类型耦合。所以我们好奇地想知道,编译器合成 CaseIterable 是使用了哪个具体的 Collection 类型呢?

enum Weekday : String, CaseIterable {
    case monday, tuesday, wednesday, thursday, friday
}

print(type(of:Weekday.allCases))

原来还是 Array

小结

本文,我们详细讨论了 CaseIterable:

  • CaseIterable 编译器合成的条件
  • 自己实现 CaseIterable
  • CaseIterable 的定义

你可能感兴趣的:(Swift 4.2 新特性详解 CaseIterable.allCases)