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

像这样声明枚举确实有效,但try 将两个或两个值合并在一起无法编译:

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

我查看SWIFT导入基础EnUM类型,它通过定义符合RawOptionSet协议的struct来实现:

struct NSCalendarUnit : RawOptionSet {
    init(_ value: UInt)
    var value: UInt
    static var CalendarUnitEra: NSCalendarUnit { get }
    static var CalendarUnitYear: NSCalendarUnit { get }
    // ...
}

RawOptionSet协议是:

protocol RawOptionSet : LogicValue, Equatable {
    class func fromMask(raw: Self.RawType) -> Self
}

然而,没有关于这个协议的文档,我自己也不知道如何实现它.此外,目前还不清楚这是否是实现位字段的官方快捷方式,或者这是否只是Objective-C桥如何表示它们.

推荐答案

更新为Swift 2/3

自swift 2以来,一个新的解决方案被添加为"原始选项集"(see: Documentation),这与我最初的响应基本相同,但使用允许任意值的 struct .

这是最初的问题,改写为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]

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

m1.union(.Four).rawValue // Produces 5

与C-类似功能中的One | Four相同.至于One & Mask != 0,可以指定为非空交叉口

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

奇怪的是,大多数C风格的按位枚举在Swift 3上都被转换成了OptionSet,但Calendar.Compontentsgo 掉了Set<Enum>:

let compontentKeys : Set<Calendar.Component> = [.day, .month, .year]

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

最初的答复

我认为最好的办法是避免使用位掩码语法,直到Swift开发人员找到更好的方法.

大多数情况下,这个问题可以用enumSet来解决

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

var options = Set<Options>(arrayLiteral: .A, .D)

and check(options & .A)可定义为:

options.contains(.A)

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

options.isSupersetOf(Set<Options>(arrayLiteral: .A, .D))

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

options.insert(.C)

这还允许使用枚举的所有新功能:自定义类型、与switch case匹配的模式等.

当然,它没有按位操作的效率,也不兼容低级操作(比如发送蓝牙命令),但对于UI元素来说,UI的开销超过了设置操作的成本,这是很有用的.

Swift相关问答推荐

try 在小部件内部读取核心数据的SwiftUI总是返回空吗?

我可以在预览中通过拖动手势跳转,但在模拟器中失败

在按下按钮动画后执行按钮动作

插入/删除视图时的Swiftui动态过渡

如何偏移HStack中的视图,但仍然约束框架以与偏移匹配?

按数组大小进行类型判断?

为什么自定义串行队列的目标是全局队列时,Swift中不会并发执行?

在 Swift 中将异步任务包装到 DispatchWorkItem 中以使其可取消?

避免 `(())` 显式指定 Void 类型的枚举关联值

swiftui中服务端图片如何设计view并赋予rotationEffect?

在 Swift 的异步上下文中,如何指示我想要函数的同步版本?

从 Swift Vapor Server 中调用 ShazamKit

如何在 SwiftUI 中将图像传递到 2 个视图

在 RealityKit 中创建转场

如何仅将 SwiftUI 不透明度应用于父视图?

Swift 5.0 编译器无法导入使用 Swift 4.2.1 编译的模块

swift : 具有类型和值的枚举常量

Swift - 迭代 struct 对象时如何对其进行变异

TabView 在切换选项卡时重置导航堆栈

SwiftUI:将多个 BindableObjects 放入环境