Help us understand the problem. What is going on with this article?

【Swift】複数のフラグを管理する場合に Set<Enum> を使う

この記事は

複数のフラグを管理する場合、データサイズを固定長にする目的で OptionSet を使うことがあります。
しかしフラグの数が少なく、データサイズを気にしない状況であれば、OptionSet のかわりに Set<Enum> を使うことで実装を少しシンプルにできます。

データ型の定義

OptionSet

OptionSet はビット集合を表現したプロトコルです。
true | false )を整数型の各ビットの( 1 | 0 )に対応させることで、複数のフラグを1つの 整数型で表現します。

struct ShippingOptions: OptionSet {
    let rawValue: Int

    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)
}

Set<Enum>

Set の要素である列挙体はシンプルに次のように定義するだけです。

enum ShippingOptions {
    case nextDay
    case secondDay
    case priority
    case standard
}

Set<Enum> は列挙型の値を要素をもつ集合です。集合内の要素の有無によってフラグを表現します。
OptionSet の場合とは異なり、要素をビットに格納する方法を用いないので、raw-value-style-enum である必要はありません。もちろん、raw-value-style-enum でも良いです。
rawValue の型は何でも良く、Int である必要もありません。

全体集合

OptionSet では全体集合を自前で定義する必要がありますが、
列挙型であれば CaseIterable で自動合成可能です。

OptionSet

struct ShippingOptions: OptionSet {
    let rawValue: Int

    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 all: ShippingOptions = [nextDay, secondDay, priority, standard]
}

Set<Enum>

enum ShippingOptions: CaseIterable {
    case nextDay
    case secondDay
    case priority
    case standard
}

allCases から Set を作ることで全体集合になります。

let universe = Set(ShippingOptions.allCases)
// true
universe.isSuperset(of: [.nextDay, .secondDay])

使い方

OptionSetSet<Enum> はともに SetAlgebra に適合しているので、どちらも集合演算を使用することができます。フラグの設定・取得はほとんど同じ操作です。

OptionSet

var options = ShippingOptions()
options.formUnion([.nextDay, .priority])

if (options == [.nextDay, .priority]) {
    print("お急ぎ")
}

Set<Enum>

var options = Set<ShippingOptions>()
options.formUnion([.nextDay, .priority])

if (options == [.nextDay, .priority]) {
    print("お急ぎ")
}

複数のフラグを OptionSet で定義する理由

Set<Enum> の方がシンプルに定義できたりと優位性があるように見えますが、UIKit などのフレームワークでは OptionSet で定義されています。

Swift で作った Set<Enum> では Objective-C にブリッジすることができないからです。
しかし、Objective-C で作った NS_OPTIONS であれば OptionSet な構造体として Swift にブリッジすることができます。

typedef NS_OPTIONS(NSInteger, ShippingOptions) {
    ShippingOptionsNextDay   = 1 << 0,
    ShippingOptionsSecondDay = 1 << 1,
    ShippingOptionsPriority  = 1 << 2,
    ShippingOptionsStandard  = 1 << 3,

    ShippingOptionsAll = 0b1111,
};

Objective-C で上記の定義をすることで、Swift では次のようにインポートされます。

public struct ShippingOptions: OptionSet {
    public init(rawValue: Int)

    public static var nextDay:   ShippingOptions { get }
    public static var secondDay: ShippingOptions { get }
    public static var priority:  ShippingOptions { get }
    public static var standard:  ShippingOptions { get }

    public static var all: ShippingOptions { get }
}

Objective-C と Swift の両方をサポートする場合は、NS_OPTIONS で定義することになります。

まとめ

  • OptionSet は複数のフラグを固定長データに詰め込むことができる
  • Set でも OptionSet と同じ操作ができる
  • Set で実装した方がシンプル
  • Objective-C と Swift の両方をサポートしたい場合は NS_OPTIONS で実装する
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした