快速,更优雅的代码: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例如Int8Int16 。 接下来,您需要静态地声明所需的选项,然后猜猜是什么? 这就是创建选项集所要做的全部!

好吧,我想假设您仍然对自己声明选项有些困惑,您可能会想到“为什么它必须是静态的? 什么是<<东西?”

补充工具栏注意 :如果您不那么了解,那么在直接进入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 ,您会看到singlePet21 << 1 ), multiplePets31 << 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协议。

作者✍️

欣赏您的反馈👏敬请关注更多“快速,优雅的代码”主题。

谢谢阅读!