快速,更优雅的代码:OptionSet
您是否想知道如何在Swift中处理Bitmasks? 您是否听说过 OptionSet
并且想了解更多? 好吧,这是给你的!
什么是OptionSet?
基本上,OptionSet是一种符合它的协议,使您能够进行位屏蔽。 它是位集的表示,每个位都代表一个选项。 听起来不清楚? 让我们编写代码:
如何实现OptionSet?
这是一个符合OptionSet
的Structure的基本实现:
struct MyStruct:OptionSet {
let rawValue:Int静态let firstOption = MyStruct(rawValue:1 << 0)
静态让secondOption = MyStruct(rawValue:1 << 1)
静态让thirdOption = MyStruct(rawValue:1 << 2)
}
让我们解释一下上面的代码中发生了什么:
符合OptionSet
要求声明rawValue
,它不必是Int
类型,而是任何类型的FixedWidthInteger
例如Int8
或Int16
。 接下来,您需要静态地声明所需的选项,然后猜猜是什么? 这就是创建选项集所要做的全部!
好吧,我想假设您仍然对自己声明选项有些困惑,您可能会想到“为什么它必须是静态的? 什么是<<
东西?”
补充工具栏注意 :如果您不那么了解,那么在直接进入OptionSet选项声明之前,我建议您熟悉“一对一”的世界。 了解十进制值的二进制表示形式将使理解和实现OptionSet选项更加容易。 这是一篇很酷的文章:位,字节,用二进制构建。
从逻辑上讲,应该将选项声明为static
,因为您可以直接访问它,而无需从一致的结构(在我们的示例中为MyStruct
)实例化-稍后我们将看到。 接下来,应将选项声明为一致结构的实例(在我们的示例中为MyStruct),并为其分配所需的“二进制计数” rawValue,这就是使用<<
左移运算符的目的。
尽管我建议您查看按位运算符的文档-按位左移和向右移位运算符,但我还是要简要介绍一下它的工作原理:它将所有数字向左移动,例如:
让一个= 0b0001 // 1
让shiftedZero =一<< 2 // 4(0b0100)let二十一= 0b0010101 // 21
let shiftedTewntyOne =二十一<< 2 // 84(1010100)
我们只是将位移动了两个步骤。
所以是的,将选项实现为:
静态让firstOption = MyStruct(rawValue:1 << 0)
静态让secondOption = MyStruct(rawValue:1 << 1)
静态让thirdOption = MyStruct(rawValue:1 << 2)
等同于:
静态let firstOption = MyStruct(rawValue:1)
静态让secondOption = MyStruct(rawValue:2)
静态让thirdOption = MyStruct(rawValue:4)
那么,为什么我们要使用<<
来设置原始值呢? 确保不破坏二进制计数公式(1、2、4、8、16 …),将原始值设置为3可能会更加方便,例如,它将破坏它! 我所说的“打破它”并不意味着它会导致重现编译时错误,甚至肯定会使您的应用程序崩溃,但您不会从Option Set中受益。
为什么要使用OptionSet?
您可以简单地将其视为一个变量中的组合选项(请注意,我们并不是在谈论可能包含多个元素(例如数组)的集合,它只是一个值)。
例:
考虑我们有一个应用程序,可以让用户选择自己喜欢的宠物,它应该是以下选项之一( 猫咪 , 小狗 , 仓鼠或变色龙 )。 我们认为将此要求转换为代码的第一个选择是(可能)实现enum ,听起来是一个适当的选择:
枚举Pet {
案例猫咪,小狗,仓鼠,变色龙
}
并简单地:
设宠物:宠物= .puppy
但是,我们已经知道事情会发生巨大变化。 我们对应用程序的要求已更改为:用户可以选择多个喜欢的宠物…现在呢? 作为解决方法,我们可以执行以下操作:
让fattytiePets:[宠物] = [.pussycat,。 小狗 ]
甚至甚至可以将favortiePets
声明为Set。 尽管绝对合法,但是在某些时候将其另存为列表是不明智的,例如,如果需要将该值发送到服务器端怎么办? 会以清单形式发送吗?
好吧,让我们看看如何使用OptionSet处理它:
struct Pet:OptionSet {
let rawValue:UInt8静态let pussycat = Pet(rawValue:1 << 0)
静态let小狗= Pet(rawValue:1 << 1)
静态让仓鼠= Pet(rawValue:1 << 2)
静态let变色龙= Pet(rawValue:1 << 3)
}让singlePet:宠物= .puppy
让multiplePets:宠物= [.puppy,.pussycat]
注意, multiplePets
类型是Pet
,不是[Pet]
(不要被方括号trick欺骗)。 如果您rawValue
检查两个常量的rawValue
,您会看到singlePet
为2 ( 1 << 1
), multiplePets
为3 ( 1 << 1
+ 1 << 0
)。
通过使用OptionSet的方法,您可以仅使用一个UInt8
来表示多个选项,这意味着:
最喜欢的宠物是猫咪 : rawValue
= 1
最喜欢的宠物是仓鼠和变色龙 : rawValue
= 12(4 + 8)
最喜欢的宠物是它的全部: rawValue
= 15(1 + 2 + 4 + 8)
此外
使用OptionSet时将获得的另一个非常酷的功能是与集相关的操作 ,例如(基于Pet
struct):
让options1:宠物= [.pussycat,.puppy]
让options2:宠物= [.puppy 、.仓鼠,.chameleon]让交集= options1.intersection(options2)
print(intersection)// Pet(rawValue:2)👉puppylet union = options1.union(options2)
print(union)// Pet(rawValue:15)👉小猫,小狗,仓鼠和变色龙减法= options1.subtracting(options2)
print(减)// Pet(rawValue:1)👉pussycatlet contains = options1.contains(.hamster)
print(包含)// false
包起来:
OptionSet是实现位掩码的方法。 正如我们所看到的那样,将相关选项的有限列表存储为单个值可能是一个不错的选择。 但是,请记住要明智地使用它! 最后,它不是一个集合。
参考:
- Swift文档:OptionSet协议。
作者✍️
欣赏您的反馈👏敬请关注更多“快速,优雅的代码”主题。
谢谢阅读!