测试测试通过 | ✓ |
语言语言 | SwiftSwift |
许可证 | MIT |
发布时间最新发布 | 2016年1月 |
SPM支持 SPM | ✗ |
由 Bryn Austin Bellomy 维护。
Bitmask<T>
是在 Swift 环境中获得类似 NS_OPTIONS
功能的一个更快捷的方法。它旨在替代 Swift 的 RawOptionSet
,后者非常长且难以实现,以至于有 人编写了代码生成器来生成它。
它允许您通过将类型包裹在 Bitmask<T>
中,以 Swift 的位运算符(|
、&
、~
、^
等等)的简单、熟悉的语法使用任何自定义的 struct
、enum
或 class
类型。
使用 CocoaPods。
在您的 Podfile
中
pod 'SwiftBitmask'
然后从命令行
$ pod install
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
位掩码也可以在非整型类型之间相互转换。实现 IBitmaskRepresentable
协议的任何类型都免费获得此功能。
该协议仅要求您实现 var bitmaskValue
,确保其类型符合 BitwiseOperationsType
和 Equatable
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)
Bitmask
还实现了NilLiteralConvertible
和BooleanType
,这允许编写简洁的条件表达式。
// 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!")
}
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
对象为您IBitmaskRepresentable
类型的Bitmask
值自动生成。如果您类型是一个具有非整型原始类型的enum
,这将很有用。
例如,假设您正在从JSON配置文件中读取选项,并且它们表示为字符串数组,并且您希望您的enum
的rawValue
表示文件中的内容。通过实现IAutoBitmaskable
,您可以在类型的IBitmaskRepresentable
实现中使用一些便利函数,而不必编写将类型代码长度翻倍的大型switch语句。
只需遵守IAutoBitmaskable
并添加class/static var autoBitmaskValues : [Self]
,该属性应返回您enum
的所有值。然后您可以像这样实现var bitmaskValue
和init(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 << 0
,1 << 1
,… 1 << n
)。
值得注意的是,因为Bitmask将其原始值存储为基本的BitwiseOperationsType
(换句话说,某种形式的整数),其内存占用非常小,尤其是在与基本上作为Set
(或某些其他集合类型)包装器的实现相比时。当您同时初始化或配置大量对象时,这特别令人愉悦。
当然,权衡的是,每次您在您的IBitmaskRepresentable
类型和Bitmask
之间进行转换时,都会在某些处理开销中,访问对象的bitmaskValue
属性(如果您实现了IAutoBitmaskable
,那么还有一个数组搜索操作 —— 如果您对这一点感到担忧,请查看AutoBitmask.swift)。
我选择这种实现方式,因为我使用Bitmask
在一个游戏加载阶段初始化值。由于一旦加载完成,真正的Bitmask
对象就不再存在,所以我在我IBitmaskRepresentable
类型和Bitmask
之间来回转换的次数非常少,这意味着很少有一些处理开销。我只是保留整数值,这正是游戏引擎想要的东西。
然而,如果您有将Bitmask
实现为集合包装器的用例,我会很乐意在问题队列中听到您的想法!
ISC
bryn bellomy < [email protected] >