不要误会,我们谈的是 Options,不是 Optional。后者已经被谈论太多了,我不想再在上面再多补充什么了。

我们来说说 Options,或者在 Objective-C 中的 NS_OPTIONS,在 Swift 中是怎样的形式吧。

在 Objective-C 中,我们有很多需要提供某些选项的 API,它们一般用来控制 API 的具体的行为配置等。举个例子,常用的 UIView 动画的 API 在使用时就可以进行选项指定:

[UIView animateWithDuration:0.3
                      delay:0.0
                    options:UIViewAnimationOptionCurveEaseIn |
                            UIViewAnimationOptionAllowUserInteraction
                 animations:^{
    // ...
} completion:nil];

我们可以使用 | 或者 & 这样的按位逻辑符对这些选项进行操作,这是因为一般来说在 Objective-C 中的 Options 的定义都是类似这样的按位错开的:

typedef NS_OPTIONS(NSUInteger, UIViewAnimationOptions) {
    UIViewAnimationOptionLayoutSubviews            = 1 <<  0,
    UIViewAnimationOptionAllowUserInteraction      = 1 <<  1,
    UIViewAnimationOptionBeginFromCurrentState     = 1 <<  2,

    //...

    UIViewAnimationOptionTransitionFlipFromBottom  = 7 << 20,
}

通过一个 typedef 的定义,我们可以使用 NS_OPTIONS 来把 UIViewAnimationOptions 映射为每一位都不同的一组 NSUInteger。不仅是这个动画的选项如此,其他的 Option 值也都遵循着相同的规范映射到整数上。如果我们不需要特殊的什么选项的话,可以使用 kNilOptions 作为输入,它被定义为数字 0。

    enum {
      kNilOptions = 0
    };

在 Swift 中,对于原来的枚举类型 NS_ENUM 我们有新的 enum 类型来对应。但是原来的 NS_OPTIONS 在 Swift 里显然没有枚举类型那样重要,并没有直接的原生类型来进行定义。原来的 Option 值现在被映射为了满足 OptionSetType 接口的 struct 类型,以及一组静态的 get 属性:

public struct UIViewAnimationOptions : OptionSetType {
    public init(rawValue: UInt)
    static var LayoutSubviews: UIViewAnimationOptions { get }
    static var AllowUserInteraction: UIViewAnimationOptions { get }

    //...

    static var TransitionFlipFromBottom: UIViewAnimationOptions { get }
}

这样一来,我们就可以用和原来类似的方式为方法指定选项了。用 Swift 重写上面的 UIView 动画的代码的话,我们可以使用这个新的 struct 的值。在使用时,可以用生成集合的方法来制定符合“或”逻辑多个选项:

UIView.animateWithDuration(0.3,
    delay: 0.0,
    options: [.CurveEaseIn, .AllowUserInteraction],
    animations: {},
    completion: nil)

OptionSetType 是实现了 SetAlgebraType 的,因此我们可以对两个集合进行各种集合运算,包括并集 (union)、交集 (intersect) 等等。另外,对于不需要选项输入的情况,也就是对应原来的 kNilOptions,现在我们直接使用一个空的集合 [] 来表示:

UIView.animateWithDuration(0.3,
    delay: 0.0,
    options: [],
    animations: {},
    completion: nil)

要实现一个 Options 的 struct 的话,可以参照已有的写法建立类并实现 OptionSetType。因为基本上所有的 Options 都是很相似的,所以最好是准备一个 snippet 以快速重用:

struct YourOption: OptionSetType {
    let rawValue: UInt
    static let None = YourOption(rawValue: 0)
    static let Option1 = YourOption(rawValue: 1)
    static let Option2 = YourOption(rawValue: 1 << 1)
    //...
}