🌟
Swift OptionSet(Macro)の使い方
OptionSet Protocol
OptionSet Protocolとは1つの型でArrayのように値を管理できるSwiftのprotocolです。
let singleOption: ShippingOptions = .priority
let multipleOptions: ShippingOptions = [.nextDay, .secondDay, .priority]
let noOptions: ShippingOptions = []
SwiftUIだとEdge.Setなどで使われています。
Text("Hello World!")
.padding([.leading, .trailing], 16)
OptionSetの定義方法
OptionSetにはRawValueが必要です。またinit(rawValue:)
というInitializerも必要です。
struct ShippingOptions: OptionSet {
typealias RawValue = UInt8
let rawValue: RawValue
init(rawValue: RawValue) {
self.rawValue = rawValue
}
static let nextDay = ShippingOptions(rawValue: 1 << 0)
static let secondDay = ShippingOptions(rawValue: 1 << 1)
static let priority = ShippingOptions(rawValue: 1 << 2)
static let standard = ShippingOptions(rawValue: 1 << 3)
static let express: ShippingOptions = [.nextDay, .secondDay]
static let all: ShippingOptions = [.express, .priority, .standard]
}
Macroを使ったOptionSetの定義方法
上の宣言をMacroを使うと簡単に実装可能です。
@OptionSet<UInt8>
struct ShippingOptions {
private enum Options: UInt8 {
case nextDay
case secondDay
case priority
case standard
}
}
OptionSetMacroはそのまま使えるように定義されていないため、自分で定義しないといけません。(Swift 6.1時点)
@attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary)
@attached(extension, conformances: OptionSet)
public macro OptionSet<RawType>() = #externalMacro(module: "SwiftMacros", type: "OptionSetMacro")
OptionSetの注意点
1. オーバーフローしてしまう
OptionSetはそれぞれの値を各ビットに格納しているため、UInt8の場合8ビットのみ格納のため、8つのパターンまでしか持つことができません。それ以上もつと値がオーバーフローしてしまいrawValueが0のパターンが複数持つことになってしまいます。
下の例ではp8, p9がオーバーフローしてしまい、rawValueが0になってしまいます。
UInt16にすれば16個までパターンを持つことが可能です。
@OptionSet<UInt8>
struct Parameters {
enum Options: UInt8 {
case p0
case p1
case p2
case p3
case p4
case p5
case p6
case p7
case p8
case p9
}
}
print(Parameters.p0.rawValue) // 1
print(Parameters.p1.rawValue) // 2
print(Parameters.p2.rawValue) // 4
print(Parameters.p3.rawValue) // 8
print(Parameters.p4.rawValue) // 16
print(Parameters.p5.rawValue) // 32
print(Parameters.p6.rawValue) // 64
print(Parameters.p7.rawValue) // 128
print(Parameters.p8.rawValue) // 0
print(Parameters.p9.rawValue) // 0
2. RawValueに意味を持たせてはいけない
RawValueは各範囲の値を持っているだけなので、rawValueに意味のあるようなデータには使用できません。
そのためrawValueの参照が必要なプログラムには利用不可です。
enum Signal: UInt8 {
case powerOn = 0
case powerOff = 13
case sendData = 18
}
利用可能な場面
rawValueの指定をせずに、使えると判断できる場合のみ利用可能です。
持たせられるパターン数がデータサイズによるので、注意です。UInt8だと8個までです。
@OptionSet<UInt8>
struct ShippingOptions {
private enum Options: UInt8 {
case nextDay
case secondDay
case priority
case standard
}
}
Discussion