Swift 中的選項集合
對于位掩碼,Swift 給出的方案是:選項集合(option sets)。在 C 和 Objective-C 中,通常的做法是將一個布爾值選項集合表示為一系列值為 2 的整數次冪的枚舉成員。之后就可以使用位掩碼來選擇想要的選項了。舉例來說, NSString 定義了一個名為 NSStringCompareOptions 的枚舉以表示字符串比較選項:
objective-c
typedef enum {
NSCaseInsensitiveSearch = 1,
NSLiteralSearch = 2,
NSBackwardsSearch = 4,
NSAnchoredSearch = 8,
NSNumericSearch = 64,
NSDiacriticInsensitiveSearch = 128,
NSWidthInsensitiveSearch = 256,
NSForcedOrderingSearch = 512,
NSRegularExpressionSearch = 1024
} NSStringCompareOptions;
要同時使用 case-insensitive,backward search,你可以使用 按位或 來組合對應的選項:
objective-c
NSStringCompareOptions options = NSCaseInsensitiveSearch | NSBackwardsSearch;
// → 5 (= 1 + 4)
譯者注:選擇了第一個選項,可以用二進制表示為 001,也就是十進制的 1;選擇了第三個選項,可以用二進制表示為 100,也就是十進制的 4;同時選擇第一個和第三個選擇,即 101,等于 001 | 100,同時也是十進制的 5。
使用選項集合
Swift 使用 結構體( struct ) 來遵從 OptionSet 協議,以引入選項集合,而非 枚舉( enum ) 。為什么這樣處理呢?當枚舉成員互斥的時候,比如說,一次只有一個選項可以被選擇的情況下,枚舉是非常好的。但是和 C 不同,在 Swift 中,你無法把多個枚舉成員組合成一個值,而 C 中的枚舉對編譯器來說就是整型,可以接受任意整數值。
和 C 中一樣,Swift 中的選項集合結構體使用了高效的位域來表示,但是這個結構體本身表現為一個集合,它的成員則為被選擇的選項。這允許你使用標準的 集合運算 #Basic_operations)來維護位域,比如使用 contains 來檢驗集合中是否有某個成員,或者是用 union 來組合兩個位域。另外,由于 OptionSet 繼承于 ExpressibleByArrayLiteral ,你可以使用數組字面量來生成一個選項集合。
let options: NSString.CompareOptions = [.caseInsensitive, .backwards]
options.contains(.backwards) // → true
options.contains(.regularExpression) // → false
options.union([.diacriticInsensitive]).rawValue :// → 133 (= 1 + 4 + 128)
遵從 OptionSet
如何創建你自己的選項集合類型呢?僅有的要求是,一個類型為整型的原始值( rawValue )和一個初始化構造器。對于結構體來說,Swift 通常都會自動提供一個逐一成員構造器(memberwise initializer),所以你并不需要自己寫一個。 rawValue 是位域底層的存儲單元。每個選項都應該是靜態的常量,并使用適當的值初始化了其位域。
struct Sports: OptionSet {
let rawValue: Int
static let running = Sports(rawValue: 1)
static let cycling = Sports(rawValue: 2)
static let swimming = Sports(rawValue: 4)
static let fencing = Sports(rawValue: 8)
static let shooting = Sports(rawValue: 32)
static let horseJumping = Sports(rawValue: 512)
}
現在,你可以創建選項集合了,就像這樣:
let triathlon: Sports = [.swimming, .cycling, .running]
triathlon.contains(.swimming) // → true
triathlon.contains(.fencing) // → false
需要注意的是,編譯器并沒有自動把 2 的整數次冪按照升序賦給你的選項——這些工作應該由你來做,你需要正確地賦值,使得每個選項代表 rawValue 中的其中一個位。如果你給選項賦予了連續的整數(1,2,3,...),就會導致無法分辨出 .swimming (值為 3)和 [.running, .cycling] (值為 1 + 2)。
手動賦值的好處有兩個:a. 更直觀易懂;b. 能夠完全掌控每個選項的值。這也允許你提供額外的屬性來對常用的選項進行組合:
extension Sports {
static let modernPentathlon: Sports =
[.swimming, .fencing, .horseJumping, .shooting, .running]
}
let commonEvents = triathlon.intersection(.modernPentathlon)
commonEvents.contains(.swimming) // → true
commonEvents.contains(.cycling) // → false
選項集合并不是集合類型
遵從 OptionSet 并不意味著遵從 Sequence 和 Collection 協議,所以你無法使用 count 來確定集合中有幾個元素,也無法使用 for 循環來遍歷選擇的選項。從根本上說,一個選項集合僅僅是簡單的整數值。
來自:https://segmentfault.com/a/1190000007278052