ios 如何在 Swift 中创建 NS_OPTIONS 样式的位掩码枚举?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24066170/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 00:32:43  来源:igfitidea点击:

How to create NS_OPTIONS-style bitmask enumerations in Swift?

iosbitwise-operatorsswift

提问by Nate Cook

In Apple's documentation about interacting with C APIs, they describe the way NS_ENUM-marked C-style enumerations are imported as Swift enumerations. This makes sense, and since enumerations in Swift are readily provided as the enumvalue type it's easy to see how to create our own.

在 Apple 关于与 C API 交互的文档中,他们描述了将带NS_ENUM标记的 C 样式枚举作为 Swift 枚举导入的方式。这是有道理的,因为 Swift 中的枚举很容易作为enum值类型提供,所以很容易看到如何创建我们自己的。

Further down, it says this about NS_OPTIONS-marked C-style options:

再往下,它说的是关于NS_OPTIONS-marked C 风格的选项:

Swift also imports options marked with the NS_OPTIONSmacro. Whereas options behave similarly to imported enumerations, options can also support some bitwise operations, such as &, |, and ~. In Objective-C, you represent an empty option set with the constant zero (0). In Swift, use nilto represent the absence of any options.

Swift 还导入标有NS_OPTIONS宏的选项。而选项的行为类似于进口枚举,选项还可以支持一些位操作,如&|~。在 Objective-C 中,您用常量零 ( 0)表示一个空选项集。在 Swift 中,用于nil表示没有任何选项。

Given that there isn't an optionsvalue type in Swift, how can we create a C-Style options variable to work with?

鉴于optionsSwift 中没有值类型,我们如何创建一个 C 风格的选项变量来使用?

回答by Nate Cook

Swift 3.0

斯威夫特 3.0

Almost identical to Swift 2.0. OptionSetType was renamed to OptionSet and enums are written lower case by convention.

几乎与 Swift 2.0 相同。OptionSetType 被重命名为 OptionSet 并且枚举按照约定写成小写。

struct MyOptions : OptionSet {
    let rawValue: Int

    static let firstOption  = MyOptions(rawValue: 1 << 0)
    static let secondOption = MyOptions(rawValue: 1 << 1)
    static let thirdOption  = MyOptions(rawValue: 1 << 2)
}

Instead of providing a noneoption, the Swift 3 recommendation is to simply use an empty array literal:

noneSwift 3 的建议不是提供选项,而是简单地使用空数组文字:

let noOptions: MyOptions = []

Other usage:

其他用法:

let singleOption = MyOptions.firstOption
let multipleOptions: MyOptions = [.firstOption, .secondOption]
if multipleOptions.contains(.secondOption) {
    print("multipleOptions has SecondOption")
}
let allOptions = MyOptions(rawValue: 7)
if allOptions.contains(.thirdOption) {
    print("allOptions has ThirdOption")
}

Swift 2.0

斯威夫特 2.0

In Swift 2.0, protocol extensions take care of most of the boilerplate for these, which are now imported as a struct that conforms to OptionSetType. (RawOptionSetTypehas disappeared as of Swift 2 beta 2.) The declaration is far simpler:

在 Swift 2.0 中,协议扩展负责处理这些的大部分样板,这些样板现在作为符合OptionSetType. (RawOptionSetType从 Swift 2 beta 2 开始已经消失了。)声明要简单得多:

struct MyOptions : OptionSetType {
    let rawValue: Int

    static let None         = MyOptions(rawValue: 0)
    static let FirstOption  = MyOptions(rawValue: 1 << 0)
    static let SecondOption = MyOptions(rawValue: 1 << 1)
    static let ThirdOption  = MyOptions(rawValue: 1 << 2)
}

Now we can use set-based semantics with MyOptions:

现在我们可以使用基于集合的语义MyOptions

let singleOption = MyOptions.FirstOption
let multipleOptions: MyOptions = [.FirstOption, .SecondOption]
if multipleOptions.contains(.SecondOption) {
    print("multipleOptions has SecondOption")
}
let allOptions = MyOptions(rawValue: 7)
if allOptions.contains(.ThirdOption) {
    print("allOptions has ThirdOption")
}

Swift 1.2

斯威夫特 1.2

Looking at the Objective-C options that were imported by Swift (UIViewAutoresizing, for example), we can see that options are declared as a structthat conforms to protocol RawOptionSetType, which in turn conforms to _RawOptionSetType, Equatable, RawRepresentable, BitwiseOperationsType, and NilLiteralConvertible. We can create our own like this:

看着由夫特导入的(Objective-C的选项UIViewAutoresizing,例如),我们可以看到,选项被声明为struct符合协议RawOptionSetType,这反过来又符合 _RawOptionSetTypeEquatableRawRepresentableBitwiseOperationsType,和NilLiteralConvertible。我们可以像这样创建自己的:

struct MyOptions : RawOptionSetType {
    typealias RawValue = UInt
    private var value: UInt = 0
    init(_ value: UInt) { self.value = value }
    init(rawValue value: UInt) { self.value = value }
    init(nilLiteral: ()) { self.value = 0 }
    static var allZeros: MyOptions { return self(0) }
    static func fromMask(raw: UInt) -> MyOptions { return self(raw) }
    var rawValue: UInt { return self.value }

    static var None: MyOptions { return self(0) }
    static var FirstOption: MyOptions   { return self(1 << 0) }
    static var SecondOption: MyOptions  { return self(1 << 1) }
    static var ThirdOption: MyOptions   { return self(1 << 2) }
}

Now we can treat this new option set, MyOptions, just like described in Apple's documentation: you can use enum-like syntax:

现在我们可以MyOptions像 Apple 的文档中描述的那样对待这个新的选项集:您可以使用enum-like 语法:

let opt1 = MyOptions.FirstOption
let opt2: MyOptions = .SecondOption
let opt3 = MyOptions(4)

And it also behaves like we'd expect options to behave:

它的行为也像我们期望的选项一样:

let singleOption = MyOptions.FirstOption
let multipleOptions: MyOptions = singleOption | .SecondOption
if multipleOptions & .SecondOption != nil {     // see note
    println("multipleOptions has SecondOption")
}
let allOptions = MyOptions.fromMask(7)   // aka .fromMask(0b111)
if allOptions & .ThirdOption != nil {
    println("allOptions has ThirdOption")
}

I've built a generator to create a Swift option setwithout all the find/replacing.

我已经构建了一个生成器来创建一个没有所有查找/替换的 Swift 选项集

Latest:Modifications for Swift 1.1 beta 3.

最新:Swift 1.1 beta 3 的修改。

回答by Klaas

Xcode 6.1 Beta 2 brought some changes to the RawOptionSetTypeprotocol (see this Airspeedvelocity blog entryand the Apple release notes).

Xcode 6.1 Beta 2 对RawOptionSetType协议进行了一些更改(请参阅此Airspeedvelocity 博客条目Apple 发行说明)。

Based on Nate Cooks example here is an updated solution. You can define your own option set like this:

基于 Nate Cooks 示例,这里是一个更新的解决方案。您可以像这样定义自己的选项集:

struct MyOptions : RawOptionSetType, BooleanType {
    private var value: UInt
    init(_ rawValue: UInt) { self.value = rawValue }

    // MARK: _RawOptionSetType
    init(rawValue: UInt) { self.value = rawValue }

    // MARK: NilLiteralConvertible
    init(nilLiteral: ()) { self.value = 0}

    // MARK: RawRepresentable
    var rawValue: UInt { return self.value }

    // MARK: BooleanType
    var boolValue: Bool { return self.value != 0 }

    // MARK: BitwiseOperationsType
    static var allZeros: MyOptions { return self(0) }

    // MARK: User defined bit values
    static var None: MyOptions          { return self(0) }
    static var FirstOption: MyOptions   { return self(1 << 0) }
    static var SecondOption: MyOptions  { return self(1 << 1) }
    static var ThirdOption: MyOptions   { return self(1 << 2) }
    static var All: MyOptions           { return self(0b111) }
}

It can then be used like this to define variables:

然后可以像这样使用它来定义变量:

let opt1 = MyOptions.FirstOption
let opt2:MyOptions = .SecondOption
let opt3 = MyOptions(4)

And like this to test for bits:

像这样测试位:

let singleOption = MyOptions.FirstOption
let multipleOptions: MyOptions = singleOption | .SecondOption
if multipleOptions & .SecondOption {
    println("multipleOptions has SecondOption")
}

let allOptions = MyOptions.All
if allOptions & .ThirdOption {
    println("allOptions has ThirdOption")
}

回答by Tomasz B?k

Swift 2.0 example from the documentation:

文档中的 Swift 2.0 示例:

struct PackagingOptions : OptionSetType {
    let rawValue: Int
    init(rawValue: Int) { self.rawValue = rawValue }

    static let Box = PackagingOptions(rawValue: 1)
    static let Carton = PackagingOptions(rawValue: 2)
    static let Bag = PackagingOptions(rawValue: 4)
    static let Satchel = PackagingOptions(rawValue: 8)
    static let BoxOrBag: PackagingOptions = [Box, Bag]
    static let BoxOrCartonOrBag: PackagingOptions = [Box, Carton, Bag]
}

You can find it here

你可以在这里找到

回答by rickster

In Swift 2 (currently beta as part of the Xcode 7 beta), NS_OPTIONS-style types are imported as subtypes of the new OptionSetTypetype. And thanks to the new Protocol Extensionsfeature and the way OptionSetTypeis implemented in the standard library, you can declare your own types that extend OptionsSetTypeand get all the same functions and methods that imported NS_OPTIONS-style types get.

在 Swift 2(目前是 Xcode 7 beta 的一部分)中,NS_OPTIONS-style 类型作为新OptionSetType类型的子类型导入。并且由于新的协议扩展特性和OptionSetType标准库中实现的方式,你可以声明你自己的类型来扩展OptionsSetType和获得所有相同的函数和方法,这些函数和方法与导入的NS_OPTIONS类型一样。

But those functions aren't based on bitwise arithmetic operators anymore. That working with a set of non-exclusive Boolean options in C requires masking and twiddling bits in a field is an implementation detail. Really, a set of options is a set... a collection of unique items. So OptionsSetTypegets all the methods from the SetAlgebraTypeprotocol, like creation from array literal syntax, queries like contains, masking with intersection, etc. (No more having to remember which funny character to use for which membership test!)

但这些函数不再基于按位算术运算符。在 C 中使用一组非排他的布尔选项需要在字段中进行掩码和处理位是一个实现细节。真的,一组选项就是一……独特项目的集合。所以OptionsSetTypeSetAlgebraType协议中获取所有方法,比如从数组文字语法创建,查询之类的contains,用 屏蔽intersection等等(不再需要记住哪个有趣的字符用于哪个成员资格测试!)

回答by PhuocLuong

//Swift 2.0
 //create
    struct Direction : OptionSetType {
        let rawValue: Int
        static let None   = Direction(rawValue: 0)
        static let Top    = Direction(rawValue: 1 << 0)
        static let Bottom = Direction(rawValue: 1 << 1)
        static let Left   = Direction(rawValue: 1 << 2)
        static let Right  = Direction(rawValue: 1 << 3)
    }
//declare
var direction: Direction = Direction.None
//using
direction.insert(Direction.Right)
//check
if direction.contains(.Right) {
    //`enter code here`
}

回答by Gregory Higley

If you don't need to interoperate with Objective-C and just want the surface semanticsof bit masks in Swift, I've written a simple "library" called BitwiseOptions that can do this with regular Swift enumerations, e.g.:

如果您不需要与 Objective-C 进行互操作并且只想要Swift 中位掩码的表面语义,我已经编写了一个名为 BitwiseOptions 的简单“库”,它可以使用常规的 Swift 枚举来做到这一点,例如:

enum Animal: BitwiseOptionsType {
    case Chicken
    case Cow
    case Goat
    static let allOptions = [.Chicken, .Cow, .Goat]
}

var animals = Animal.Chicken | Animal.Goat
animals ^= .Goat
if animals & .Chicken == .Chicken {
    println("Chick-Fil-A!")
}

and so on. No actual bits are being flipped here. These are set operations on opaque values. You can find the gist here.

等等。这里没有翻转实际的位。这些是对不透明值的设置操作。你可以在这里找到要点。

回答by Simple99

If the only functionality we are needing is a way to combine options with |and check if combined options contain a particular option with &an alternative to Nate Cook's answer could be this:

如果我们需要的唯一功能是将选项与组合选项|并检查组合选项是否包含特定选项以及&Nate Cook 答案的替代方法,则可能是这样的:

Create an options protocoland overload |and &:

创建一个选项protocol和重载|&

protocol OptionsProtocol {

    var value: UInt { get }
    init (_ value: UInt)

}

func | <T: OptionsProtocol>(left: T, right: T) -> T {
    return T(left.value | right.value)
}

func & <T: OptionsProtocol>(left: T, right: T) -> Bool {
    if right.value == 0 {
        return left.value == 0
    }
    else {
        return left.value & right.value == right.value
    }
}

Now we can create options structs more simply like so:

现在我们可以像这样更简单地创建选项结构:

struct MyOptions: OptionsProtocol {

    private(set) var value: UInt
    init (_ val: UInt) {value = val}

    static var None: MyOptions { return self(0) }
    static var One: MyOptions { return self(1 << 0) }
    static var Two: MyOptions { return self(1 << 1) }
    static var Three: MyOptions { return self(1 << 2) }
}

They can be used as follows:

它们可以按如下方式使用:

func myMethod(#options: MyOptions) {
    if options & .One {
        // Do something
    }
}

myMethod(options: .One | .Three) 

回答by Jarrod Smith

Just posting an extra example for anyone else who was wondering if you could combine compound options. You can, and they combine like you'd expect if you're used to good old bitfields:

只是为其他想知道您是否可以组合复合选项的人发布一个额外的示例。你可以,如果你习惯了旧的位域,它们会像你期望的那样组合:

struct State: OptionSetType {
    let rawValue: Int
    static let A      = State(rawValue: 1 << 0)
    static let B      = State(rawValue: 1 << 1)
    static let X      = State(rawValue: 1 << 2)

    static let AB:State  = [.A, .B]
    static let ABX:State = [.AB, .X]    // Combine compound state with .X
}

let state: State = .ABX
state.contains(.A)        // true
state.contains(.AB)       // true

It flattens the set [.AB, .X]into [.A, .B, .X](at least semantically):

它将集合展平[.AB, .X][.A, .B, .X](至少在语义上):

print(state)      // 0b111 as expected: "State(rawValue: 7)"
print(State.AB)   // 0b11 as expected: "State(rawValue: 3)"

回答by Antoine

As Rickster already mentioned, you can use OptionSetTypein Swift 2.0. NS_OPTIONS types get imported as conforming to the OptionSetTypeprotocol, which presents a set-like interface for options:

正如 Rickster 已经提到的,您可以在 Swift 2.0 中使用OptionSetType。NS_OPTIONS 类型被导入为符合OptionSetType协议,它为选项提供了一个类似 set 的接口:

struct CoffeeManipulators : OptionSetType {
    let rawValue: Int
    static let Milk     = CoffeeManipulators(rawValue: 1)
    static let Sugar    = CoffeeManipulators(rawValue: 2)
    static let MilkAndSugar = [Milk, Sugar]
}

It gives you this way of working:

它为您提供了这种工作方式:

struct Coffee {
    let manipulators:[CoffeeManipulators]

    // You can now simply check if an option is used with contains
    func hasMilk() -> Bool {
        return manipulators.contains(.Milk)
    }

    func hasManipulators() -> Bool {
        return manipulators.count != 0
    }
}

回答by SwiftArchitect

In order to avoid hard coding the bit positions, which is unavoidable when using (1 << 0), (1 << 1), (1 << 15)etc. or even worse 1, 2, 16384etc. or some hexadecimal variation, one could first defines the bits in an enum, then let said enum do the bit ordinal calculation:

为了避免硬编码的比特位置,使用时,其是不可避免的(1 << 0)(1 << 1)(1 << 15)等,或者甚至更糟1216384等,或一些十六进制变化,人们可以首先定义在所述位enum,然后让所述枚举做位序数的计算:

// Bits
enum Options : UInt {
    case firstOption
    case secondOption
    case thirdOption
}

// Byte
struct MyOptions : OptionSet {
    let rawValue: UInt

    static let firstOption  = MyOptions(rawValue: 1 << Options.firstOption.rawValue)
    static let secondOption = MyOptions(rawValue: 1 << Options.secondOption.rawValue)
    static let thirdOption  = MyOptions(rawValue: 1 << Options.thirdOption.rawValue)
}