Swiftのenum
は便利ですが,たまには使い慣れたNS_OPTIONS
スタイルのenum
を作りたくなるときがあります.
素朴にやるとこのように実装できます.
/// My Options enum Options: UInt { case None = 0 case A = (1 << 0) case B = (1 << 1) }
しかし,これでは全ての組み合わせのパターンをcase
として用意する必要がありますし,用意したところで常にrawValue
での比較が必要です.
// My Options doesn't have it! let options: Options = Options(rawValue: .A.rawValue | .B.rawValue)
こんなのはとてもじゃないけど使えません.
UIViewAutoresizingの実装を見てみる
UIKitにはNS_OPTIONS
を使ったenum
がいくつもあり,それらはSwiftでもObjective-Cのときと同じように使うことができます.
実際にはどう実装されているのでしょうか?
代表的なNS_OPTIONS
を使ったenum
である,UIViewAutoresizing
を例に実装を見てみます.
struct UIViewAutoresizing : RawOptionSetType { init(_ rawValue: UInt) init(rawValue: UInt) static var None: UIViewAutoresizing { get } static var FlexibleLeftMargin: UIViewAutoresizing { get } static var FlexibleWidth: UIViewAutoresizing { get } static var FlexibleRightMargin: UIViewAutoresizing { get } static var FlexibleTopMargin: UIViewAutoresizing { get } static var FlexibleHeight: UIViewAutoresizing { get } static var FlexibleBottomMargin: UIViewAutoresizing { get } }
素朴にenum
で実装するわけではなく,struct
で実装されています.
通常Swiftのenum
はcase
で各値を決めますが,全てstatic
なproperty
として定義されているのがおもしろいです.
Objective-Cのときと同じように使える秘密は,RawOptionSetType
にあってこのような継承関係を持っています.
RawOptionSetType | |---------------------------------------------- | | | _RawOptionSetType BitwiseOperationsType NilLiteralConvertible | |---------------- | | RawRepresentable Equatable
これらを実装するとこうなります.
/// My Options struct Options : RawOptionSetType { typealias RawValue = UInt private var value: UInt = 0 init(_ value: UInt) { self.value = value } init(rawValue value: UInt) { self.value = value } init(nilLiteral: ()) { self.value = 0 } static var allZeros: Options { return self(0) } static func fromMask(raw: UInt) -> Options { return self(raw) } var rawValue: UInt { return self.value } static var None: Options { return self(0) } static var A: Options { return Options(1 << 0) } static var B: Options { return Options(1 << 1) } }
これでUIViewAutoresizing
と同じように使えます.
let options: Options = .A | .B
大変な実装をすることでようやくNS_OPTIONS
相当のstruct
を手に入れることができました.
深淵なるSwiftのテクニックに触れることができて,大満足ですね.
お得情報コーナー
こんなに実装してらんねえ!と思ったアナタに朗報です! Bridging Headerにこのように書いてみましょう.
typedef NS_OPTIONS(Options, NSUInteger) {
OptionsNone,
OptionsA,
OptionsB,
};
これで大変な実装なしにSwiftにexportされます. お手軽ですね!
pure Swiftで実装したいというアナタにはこれがオススメです.
このジェネレータを使えばメッチャ簡単にNS_OPTIONS
相当のstruct
の実装が作れます.
参考
RawOptionSetType
についての解説です.
かなり初期に書かれたものなので,現在は微妙に仕様が変わっていますが参考になるとおもいます.