在Swift中声明并使用位字段枚举

Pas*_*que 36 enums bit-fields swift

如何在Swift中声明和使用位字段?

声明这样的枚举确实有效,但尝试将OR 2值一起编译失败:

enum MyEnum: Int
{
    case One =      0x01
    case Two =      0x02
    case Four =     0x04
    case Eight =    0x08
}

// This works as expected
let m1: MyEnum = .One

// Compiler error: "Could not find an overload for '|' that accepts the supplied arguments"
let combined: MyEnum = MyEnum.One | MyEnum.Four
Run Code Online (Sandbox Code Playgroud)

我查看了Swift如何导入Foundation枚举类型,它通过定义struct符合RawOptionSet协议的方式来实现:

struct NSCalendarUnit : RawOptionSet {
    init(_ value: UInt)
    var value: UInt
    static var CalendarUnitEra: NSCalendarUnit { get }
    static var CalendarUnitYear: NSCalendarUnit { get }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

RawOptionSet协议是:

protocol RawOptionSet : LogicValue, Equatable {
    class func fromMask(raw: Self.RawType) -> Self
}
Run Code Online (Sandbox Code Playgroud)

但是,没有关于此协议的文档,我无法弄清楚如何自己实现它.此外,目前尚不清楚这是否是Swift实现位域的官方方式,或者这只是Objective-C桥接器如何表示它们.

Nat*_*ook 26

您可以构建struct符合RawOptionSet协议的协议,并且您将能够像内置enum类型一样使用它,但也可以使用位掩码功能.这里的答案显示了如何: Swift NS_OPTIONS样式的位掩码枚举.

  • @jowie:这不是严格*真的,因为桥接到Obj-C的标准库类型都是结构,但它们使用私有API.如果你需要在Objective-C中工作的东西,最好的选择是使用`NS_OPTIONS`宏来定义它 - 它将作为`OptionSetType`导入到Swift中,你将能够在它的两侧使用它.这座桥. (2认同)

Joh*_*pia 13

他们在其中一个WWDC视频中展示了如何做到这一点.

let combined = MyEnum.One.toRaw() | MyEnum.Four.toRaw()
Run Code Online (Sandbox Code Playgroud)

请注意,如果您指定,combined它将是Inttype并且实际上会出现编译器错误let combined: MyEnum.那是因为没有枚举值,0x05这是表达式的结果.


Can*_*Can 13

更新了Swift 2/3

从swift 2开始,添加了一个新的解决方案作为"原始选项集"(参见:文档),它与我的原始响应基本相同,但使用允许任意值的结构.

这是原来的问题重写为OptionSet:

struct MyOptions: OptionSet
{
    let rawValue: UInt8

    static let One = MyOptions(rawValue: 0x01)
    static let Two = MyOptions(rawValue: 0x02)
    static let Four = MyOptions(rawValue: 0x04)
    static let Eight = MyOptions(rawValue: 0x08)
}

let m1 : MyOptions = .One

let combined : MyOptions = [MyOptions.One, MyOptions.Four]
Run Code Online (Sandbox Code Playgroud)

结合新值可以完全按照Set操作(因此选项部分)进行.union,同样:

m1.union(.Four).rawValue // Produces 5
Run Code Online (Sandbox Code Playgroud)

同做One | Four在C-等同.至于One & Mask != 0,可以指定为非空交集

// Equivalent of A & B != 0
if !m1.intersection(combined).isEmpty
{
    // m1 belongs is in combined
}
Run Code Online (Sandbox Code Playgroud)

奇怪的是,大多数C风格的按位枚举已经OptionSet在Swift 3上转换为它们的等价物,但却Calendar.Compontents没有Set<Enum>:

let compontentKeys : Set<Calendar.Component> = [.day, .month, .year]
Run Code Online (Sandbox Code Playgroud)

原来NSCalendarUnit是一个按位枚举.所以这两种方法都是可用的(因此原始响应仍然有效)

原始回应

我认为最好的办法是简单地避免使用bitmask语法,直到Swift开发人员找到更好的方法.

大多数情况下,问题可以使用enum和和解决Set

enum Options
{
    case A, B, C, D
}

var options = Set<Options>(arrayLiteral: .A, .D)
Run Code Online (Sandbox Code Playgroud)

An和check(options & .A)可以定义为:

options.contains(.A)
Run Code Online (Sandbox Code Playgroud)

或者对于多个"标志"可以是:

options.isSupersetOf(Set<Options>(arrayLiteral: .A, .D))
Run Code Online (Sandbox Code Playgroud)

添加新标志(options |= .C):

options.insert(.C)
Run Code Online (Sandbox Code Playgroud)

这也允许使用枚举的所有新东西:自定义类型,与开关盒的模式匹配等.

当然,它没有按位操作的效率,也不兼容低级别的东西(比如发送蓝牙命令),但它对UI元素的用处是UI的开销超过了Set操作的成本.


Joh*_*nes 11

我想也许这里的一些答案已经过时了,过于复杂的解决方案?这对我来说很好..

enum MyEnum: Int  {

    case One = 0
    case Two = 1
    case Three = 2
    case Four = 4
    case Five = 8
    case Six = 16

}

let enumCombined = MyEnum.Five.rawValue | MyEnum.Six.rawValue

if enumCombined & MyEnum.Six.rawValue != 0 {
    println("yay") // prints
}

if enumCombined & MyEnum.Five.rawValue != 0 {
    println("yay again") // prints
}

if enumCombined & MyEnum.Two.rawValue != 0 {
    println("shouldn't print") // doesn't print
}
Run Code Online (Sandbox Code Playgroud)

  • 正如@lephitar在拒绝编辑中指出的那样,你的枚举是错误的:`case One`应该从1开始,而不是0.位掩码使用2和2的幂^ 0 = 1,2 ^ 1 = 2等. (11认同)

Gre*_*ley 7

如果您不需要与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!")
}
Run Code Online (Sandbox Code Playgroud)

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