Swift 中的選項集合

lewslin 8年前發布 | 6K 次閱讀 Swift Apple 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

 

 本文由用戶 lewslin 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!