SwiftBitmask 0.2.0

SwiftBitmask 0.2.0

测试测试通过
语言语言 SwiftSwift
许可证 MIT
发布时间最新发布2016年1月
SPM支持 SPM

Bryn Austin Bellomy 维护。



  • 作者:
  • bryn austin bellomy

NS_OPTIONS / RawOptionSet 的替代品

Bitmask<T> 是在 Swift 环境中获得类似 NS_OPTIONS 功能的一个更快捷的方法。它旨在替代 Swift 的 RawOptionSet,后者非常长且难以实现,以至于有 人编写了代码生成器来生成它

它允许您通过将类型包裹在 Bitmask<T> 中,以 Swift 的位运算符(|&~^ 等等)的简单、熟悉的语法使用任何自定义的 structenumclass 类型。

安装

使用 CocoaPods。

在您的 Podfile

pod 'SwiftBitmask'

然后从命令行

$ pod install

Bitmask<T> 与原始整型类型

Bitmask 类接受一个泛型参数,指示您要用于存储位掩码原始值的整型类型。代码

let bitmask = Bitmask<UInt16>(1 << 2 | 1 << 5)
bitmask.bitmaskValue  // returns UInt16(1 << 2 | 1 << 5)
let bitmaskA = Bitmask<UInt16>(1 << 2)
let bitmaskB = Bitmask<UInt16>(1 << 5)
let allTogetherNow = bitmaskA | bitmaskB
bitmask.bitmaskValue  // also returns UInt16(1 << 2 | 1 << 5), just like above

Bitmask<T> 与任何类型

位掩码也可以在非整型类型之间相互转换。实现 IBitmaskRepresentable 协议的任何类型都免费获得此功能。

该协议仅要求您实现 var bitmaskValue,确保其类型符合 BitwiseOperationsTypeEquatable

public protocol IBitmaskRepresentable
{
    typealias BitmaskRawType : BitwiseOperationsType, Equatable
    var bitmaskValue : BitmaskRawType { get }
}

只需使用任何内置的整型类型即可。

以下是一个实现 IBitmaskRepresentable 的类型快速示例

enum MonsterAttributes : UInt16, IBitmaskRepresentable
{
    case Big = 1
    case Ugly = 2
    case Scary = 4

    var bitmaskValue : BitmaskRawType { return self.rawValue }

    init(bitmaskValue: UInt16) {
        self.init(rawValue:bitmaskValue)
    }
}

现在您可以创建一个 Bitmask<MonsterAttributes> 并像使用具有原始整型底层类型的 Bitmask 一样使用它

let b = Bitmask<MonsterAttributes>(.Big, .Scary)

这能给我带来什么?

简洁的语法

您可以编写与用于简单、整数位运算的语法几乎一样简洁的代码,但这样做的好处是提高了您自定义类型的类型安全和通用性。**请注意,您几乎永远不需要编写 Bitmask<T>

// prefix operator initialization
let bitmask = |MonsterAttributes.Scary         // == Bitmask(MonsterAttributes.Scary)

// implicit initialization via bitwise operators
let option : MonsterAttributes = .Ugly
let orWithVar = option | .Big                 // == Bitmask<UInt16> with a bitmaskValue of 1 | 2
let simpleOr  = MonsterAttributes.Big | .Ugly // == Bitmask<UInt16> with a bitmaskValue of 1 | 2

// the raw bitmask value can be obtained with from the bitmaskValue property
let simpleOrValue = simpleOr.bitmaskValue                        // == UInt16(1 | 2)
let orValue       = (MonsterAttributes.Big | .Ugly).bitmaskValue // == UInt16(1 | 2)

if 语句

Bitmask还实现了NilLiteralConvertibleBooleanType,这允许编写简洁的条件表达式。

// Bitmask<T> implements BooleanType
if simpleOr & MonsterAttributes.Scary {
    println("(boolean comparison) scary!")
}

// Bitmask<T> implements NilLiteralConvertible
if simpleOr & MonsterAttributes.Scary != nil {
    println("(nil literal comparison) scary!")
}

模式匹配 + switch语句

Bitmask可以与模式匹配运算符(~=)配合使用,这基本上等同于使用&检查位是否被设置。

// Bitmask<T> is compatible with ~=, the pattern-matching operator
if simpleOr ~= MonsterAttributes.Scary {
    println("(pattern matching operator) scary!")
}

if simpleOr ~= MonsterAttributes.Scary | .Big {
    println("(pattern matching operator) either big or scary!")
}

您也可以用Bitmask编写switch语句,尽管在我的经验中,目前几乎似乎没有哪个控制结构是最合适的。如果您坚持要尝试它,请确保在适当的位置添加fallthrough,并将您的case语句按适合您应用程序的顺序排列。

switch simpleOr
{
    case |MonsterAttributes.Big:
        println("big!")
        fallthrough

    case MonsterAttributes.Big | .Scary:
        println("either big or scary!")
        fallthrough

    case |MonsterAttributes.Ugly:
        println("ugly!")
        fallthrough

    case |MonsterAttributes.Scary:
        println("scary!")
        fallthrough

    default:
        // ...
}

为懒惰和忙碌的工作者自动生成 bitmask 原始值

您还可以让您的Bitmask对象为您IBitmaskRepresentable类型的Bitmask值自动生成。如果您类型是一个具有非整型原始类型的enum,这将很有用。

例如,假设您正在从JSON配置文件中读取选项,并且它们表示为字符串数组,并且您希望您的enumrawValue表示文件中的内容。通过实现IAutoBitmaskable,您可以在类型的IBitmaskRepresentable实现中使用一些便利函数,而不必编写将类型代码长度翻倍的大型switch语句。

只需遵守IAutoBitmaskable并添加class/static var autoBitmaskValues : [Self],该属性应返回您enum的所有值。然后您可以像这样实现var bitmaskValueinit(bitmaskValue:T),使用自动 bitmasking 函数:

enum MonsterAttributes : String, IBitmaskRepresentable, IAutoBitmaskable
{
    case Big = "big"
    case Ugly = "ugly"
    case Scary = "scary"

    static var autoBitmaskValues : [MonsterAttributes] = [.Big, .Ugly, .Scary,]

    var  bitmaskValue: UInt16  { return AutoBitmask.autoBitmaskValueFor(self) }
    init(bitmaskValue: UInt16) { self = AutoBitmask.autoValueFromBitmask(bitmaskValue) }
}

如果您想知道,autoBitmaskValues数组中的值按从左到右的顺序分配整数的 bitmask 值(即,1 << 01 << 1,… 1 << n)。

一些实施细节

值得注意的是,因为Bitmask将其原始值存储为基本的BitwiseOperationsType(换句话说,某种形式的整数),其内存占用非常小,尤其是在与基本上作为Set(或某些其他集合类型)包装器的实现相比时。当您同时初始化或配置大量对象时,这特别令人愉悦。

当然,权衡的是,每次您在您的IBitmaskRepresentable类型和Bitmask之间进行转换时,都会在某些处理开销中,访问对象的bitmaskValue属性(如果您实现了IAutoBitmaskable,那么还有一个数组搜索操作 —— 如果您对这一点感到担忧,请查看AutoBitmask.swift)。

我选择这种实现方式,因为我使用Bitmask在一个游戏加载阶段初始化值。由于一旦加载完成,真正的Bitmask对象就不再存在,所以我在我IBitmaskRepresentable类型和Bitmask之间来回转换的次数非常少,这意味着很少有一些处理开销。我只是保留整数值,这正是游戏引擎想要的东西。

然而,如果您有将Bitmask实现为集合包装器的用例,我会很乐意在问题队列中听到您的想法!

许可

ISC

作者 / 贡献者

bryn bellomy < [email protected] >