web-dev-qa-db-ja.com

Swiftでビットフィールド列挙を宣言して使用する

Swiftでビットフィールドをどのように宣言して使用する必要がありますか?

このような列挙型の宣言は機能しますが、OR 2つの値を一緒にしようとするとコンパイルに失敗します。

enum MyEnum: Int
{
    case One =      0x01
    case Two =      0x02
    case Four =     0x04
    case Eight =    0x08
}

// This works as expected
let m1: MyEnum = .One

// Compiler error: "Could not find an overload for '|' that accepts the supplied arguments"
let combined: MyEnum = MyEnum.One | MyEnum.Four

SwiftがFoundation列挙型をインポートする方法を調べました。これは、structプロトコルに準拠するRawOptionSetを定義することで行います。

struct NSCalendarUnit : RawOptionSet {
    init(_ value: UInt)
    var value: UInt
    static var CalendarUnitEra: NSCalendarUnit { get }
    static var CalendarUnitYear: NSCalendarUnit { get }
    // ...
}

RawOptionSetプロトコルは次のとおりです。

protocol RawOptionSet : LogicValue, Equatable {
    class func fromMask(raw: Self.RawType) -> Self
}

ただし、このプロトコルに関するドキュメントはなく、自分でどのように実装するかわかりません。さらに、これが公式のSwiftビットフィールドを実装する方法であるか、Objective-Cブリッジがそれらを表す方法だけであるかは明らかではありません。

36
Pascal Bourque

structプロトコルに準拠するRawOptionSetを構築できます。また、組み込みのenumタイプのように使用できますが、ビットマスク機能も使用できます。ここでの答えは、 Swift NS_OPTIONS-style bitmask enumerations の方法を示しています。

26
Nate Cook

Swift 2/3に更新

Swift 2)から、新しいソリューションが「生のオプションセット」として追加されました( 参照:ドキュメント )。これは基本的に元の応答と同じですが、任意の値を許可する構造体。

これはOptionSetとして書き換えられた元の質問です:

_struct MyOptions: OptionSet
{
    let rawValue: UInt8

    static let One = MyOptions(rawValue: 0x01)
    static let Two = MyOptions(rawValue: 0x02)
    static let Four = MyOptions(rawValue: 0x04)
    static let Eight = MyOptions(rawValue: 0x08)
}

let m1 : MyOptions = .One

let combined : MyOptions = [MyOptions.One, MyOptions.Four]
_

新しい値との組み合わせは、Set操作(したがって、OptionSet部分)、_.union_とまったく同じように行うことができます。

_m1.union(.Four).rawValue // Produces 5
_

Cで同等の_One | Four_を実行するのと同じです。 _One & Mask != 0_に関しては、空でない交差として指定できます

_// Equivalent of A & B != 0
if !m1.intersection(combined).isEmpty
{
    // m1 belongs is in combined
}
_

奇妙なことに、Cスタイルのビット単位の列挙のほとんどは、Swift 3でOptionSetと同等に変換されていますが、_Calendar.Compontents_は_Set<Enum>_:

_let compontentKeys : Set<Calendar.Component> = [.day, .month, .year]
_

元のNSCalendarUnitはビット単位の列挙型でしたが。したがって、両方のアプローチが使用可能です(したがって、元の応答は有効なままです)

元の応答

最善の方法は、Swift devsがより良い方法を見つけ出すまで、ビットマスク構文を単に避けることです。

ほとんどの場合、enumSetを使用して問題を解決できます。

_enum Options
{
    case A, B, C, D
}

var options = Set<Options>(arrayLiteral: .A, .D)
_

およびチェック(_options & .A_)は次のように定義できます。

_options.contains(.A)
_

または、複数の「フラグ」の場合:

_options.isSupersetOf(Set<Options>(arrayLiteral: .A, .D))
_

新しいフラグの追加(_options |= .C_):

_options.insert(.C)
_

これにより、カスタムタイプ、スイッチケースとのパターンマッチングなど、すべての新しいものを列挙型で使用することもできます。

もちろん、ビット単位の操作の効率はなく、低レベルのもの(bluetoothコマンドの送信など)との互換性もありませんが、UIのオーバーヘッドがSet操作のコストを上回るUI要素には役立ちます。

13
Can

WWDCビデオの1つでこれを行う方法を示しました。

let combined = MyEnum.One.toRaw() | MyEnum.Four.toRaw()

combinedInt型になり、let combined: MyEnumを指定すると実際にコンパイラエラーが発生することに注意してください。これは、式の結果である0x05の列挙値がないためです。

13
John Estropia

ここでの答えのいくつかは、複雑すぎるソリューションでは時代遅れだと思いますか?これは私には問題ありません。

enum MyEnum: Int  {

    case One = 0
    case Two = 1
    case Three = 2
    case Four = 4
    case Five = 8
    case Six = 16

}

let enumCombined = MyEnum.Five.rawValue | MyEnum.Six.rawValue

if enumCombined & MyEnum.Six.rawValue != 0 {
    println("yay") // prints
}

if enumCombined & MyEnum.Five.rawValue != 0 {
    println("yay again") // prints
}

if enumCombined & MyEnum.Two.rawValue != 0 {
    println("shouldn't print") // doesn't print
}
11
Johannes

Objective-Cと相互運用する必要がなく、Swiftのビットマスクのsyntaxだけが必要な場合は、単純な「ライブラリ」を作成しました。通常のSwift列挙でこれを行うことができるBitwiseOptionsと呼ばれる、例えば:

enum Animal: BitwiseOptionsType {
    case Chicken
    case Cow
    case Goat
    static let allOptions = [.Chicken, .Cow, .Goat]
}

var animals = Animal.Chicken | Animal.Goat
animals ^= .Goat
if animals & .Chicken == .Chicken {
    println("Chick-Fil-A!")
}

等々。ここでは実際のビットは反転されていません。これらは、不透明な値に対する集合演算です。 Gist here を見つけることができます。

7
Gregory Higley

@Matttの非常に有名な「NSHipster」には、RawOptionsSetTypeの詳細な説明があります。 http://nshipster.com/rawoptionsettype/

便利なXcodeが含まれています。

struct <# Options #> : RawOptionSetType, BooleanType {
    private var value: UInt = 0
    init(_ value: UInt) { self.value = value }
    var boolValue: Bool { return value != 0 }
    static func fromMask(raw: UInt) -> <# Options #> { return self(raw) }
    static func fromRaw(raw: UInt) -> <# Options #>? { return self(raw) }
    func toRaw() -> UInt { return value }
    static var allZeros: <# Options #> { return self(0) }
    static func convertFromNilLiteral() -> <# Options #> { return self(0) }

    static var None: <# Options #>          { return self(0b0000) }
    static var <# Option #>: <# Options #>  { return self(0b0001) }
    // ...
}
3
Klaas

各メンバーの後に.toRaw()を使用する必要があります。

let combined: Int = MyEnum.One.toRaw() | MyEnum.Four.toRaw()

働くでしょう。なぜなら、あなたはMyEnum型である「One」を割り当てようとしているだけなので、not整数ではありません。 Appleのドキュメント のように:

「CおよびObjective-Cとは異なり、Swift列挙型メンバーは作成時にデフォルトの整数値を割り当てられません。CompassPointsの例では、North、South、East、Westは暗黙的に0に等しくありません。 1、2、3。代わりに、さまざまな列挙型メンバーは、明示的に定義されたタイプのCompassPointを備えた独自の完全な値です。

here で説明されているように、メンバーが他の型を表すようにする場合は、未加工の値を使用する必要があります。

列挙メンバーには、すべて同じ型のデフォルト値(生の値と呼ばれる)が事前に入力されている場合があります。特定の列挙メンバーの生の値は常に同じです。生の値は、文字列、文字、整数型または浮動小数点型のいずれかです。各生の値は、列挙宣言内で一意である必要があります。未加工の値に整数が使用されている場合、一部の列挙メンバーに値が指定されていなければ、整数が自動的に増加します。 toRawメソッドを使用して、列挙メンバーの未加工の値にアクセスします。

2
SFX

私はこのようなものがFoundationでenumオプションをモデリングしている方法だと推測しています:

_struct TestOptions: RawOptionSet {

    // conform to RawOptionSet
    static func fromMask(raw: UInt) -> TestOptions {
        return TestOptions(raw)
    }

    // conform to LogicValue
    func getLogicValue() -> Bool {
        if contains([1, 2, 4], value) {
            return true
        }
        return false
    }

    // conform to RawRepresentable
    static func fromRaw(raw: UInt) -> TestOptions? {
        if contains([1, 2, 4], raw) {
            return TestOptions(raw)
        }
        return nil
    }
    func toRaw() -> UInt {
        return value
    }

    // options and value
    var value: UInt
    init(_ value: UInt) {
        self.value = value
    }

    static var OptionOne: TestOptions {
        return TestOptions(1)
    }
    static var OptionTwo: TestOptions {
        return TestOptions(2)
    }
    static var OptionThree: TestOptions {
        return TestOptions(4)
    }
}

let myOptions = TestOptions.OptionOne | TestOptions.OptionThree
println("myOptions: \(myOptions.toRaw())")
if (myOptions & TestOptions.OptionOne) {
    println("OPTION ONE is in there")
} else {
    println("nope, no ONE")
}
if (myOptions & TestOptions.OptionTwo) {
    println("OPTION TWO is in there")
} else {
    println("nope, no TWO")
}
if (myOptions & TestOptions.OptionThree) {
    println("OPTION THREE is in there")
} else {
    println("nope, no THREE")
}

let nextOptions = myOptions | TestOptions.OptionTwo
println("options: \(nextOptions.toRaw())")
if (nextOptions & TestOptions.OptionOne) {
    println("OPTION ONE is in there")
} else {
    println("nope, no ONE")
}
if (nextOptions & TestOptions.OptionTwo) {
    println("OPTION TWO is in there")
} else {
    println("nope, no TWO")
}
if (nextOptions & TestOptions.OptionThree) {
    println("OPTION THREE is in there")
} else {
    println("nope, no THREE")
}
_

...ここでmyOptionsnextOptionsはTestOptions型です-fromMask()getLogicValue()がここでどのように動作するのか正確にはわかりません(私はちょうどいくつかの最良の推測をしました)、おそらく誰かがこれを拾ってそれを解決することができましたか?

1
fqdn

Swiftでビットフィールドが必要な場合、enumは間違った方法です。こうするのがいい

class MyBits {
    static let One =      0x01
    static let Two =      0x02
    static let Four =     0x04
    static let Eight =    0x08
}
let m1 = MyBits.One
let combined = MyBits.One | MyBits.Four

本当にクラス/静的ラッパーは必要ありませんが、一種の疑似名前空間として含めます。

1
John Henckel

以下を使用します。取得できる値、配列のインデックス付け用のrawValue、およびフラグ用の値の両方が必要です。

enum MyEnum: Int {
    case one
    case two
    case four
    case eight

    var value: UInt8 {
        return UInt8(1 << self.rawValue)
    }
}

let flags: UInt8 = MyEnum.one.value ^ MyEnum.eight.value

(flags & MyEnum.eight.value) > 0 // true
(flags & MyEnum.four.value) > 0  // false
(flags & MyEnum.two.value) > 0   // false
(flags & MyEnum.one.value) > 0   // true

MyEnum.eight.rawValue // 3
MyEnum.four.rawValue // 2
1
Peter Ahlberg

生の値を使用してビット演算を実行し、その結果を使用して新しい列挙オブジェクトを作成します。

let mask = UIViewAutoresizing(rawValue: UIViewAutoresizing.FlexibleWidth.rawValue|UIViewAutoresizing.FlexibleHeight.rawValue) self.view.autoresizingMask = mask

0
Vishun

これは、C#のフラグスタイルの列挙にある程度似たSwift列挙を作成するためにまとめたものです。しかし、私はただSwiftを学習しているので、概念実証」コード。

/// This EnumBitFlags protocol can be applied to a Swift enum definition, providing a small amount
/// of compatibility with the flags-style enums available in C#.
///
/// The enum should be defined as based on UInt, and enum values should be defined that are powers
/// of two (1, 2, 4, 8, ...). The value zero, if defined, should only be used to indicate a lack of
/// data or an error situation.
///
/// Note that with C# the enum may contain a value that does not correspond to the defined enum
/// constants. This is not possible with Swift, it enforces that only valid values can be set.
public protocol EnumBitFlags : RawRepresentable, BitwiseOperations {

   var rawValue : UInt { get }  // This provided automatically by enum

   static func createNew(_ rawValue : UInt) -> Self  // Must be defined as some boiler-plate code
}

/// Extension methods for enums that implement the EnumBitFlags protocol.
public extension EnumBitFlags {

   // Implement protocol BitwiseOperations. But note that some of these operators, especially ~, 
   // will almost certainly result in an invalid (nil) enum object, resulting in a crash.

   public static func & (leftSide: Self, rightSide: Self) -> Self {
      return self.createNew(leftSide.rawValue & rightSide.rawValue)
   }

   public static func | (leftSide: Self, rightSide: Self) -> Self {
      return self.createNew(leftSide.rawValue | rightSide.rawValue)
   }

   public static func ^ (leftSide: Self, rightSide: Self) -> Self {
      return self.createNew(leftSide.rawValue ^ rightSide.rawValue)
   }

   public static prefix func ~ (x: Self) -> Self {
      return self.createNew(~x.rawValue)
   }

   public static var allZeros: Self {
      get {
         return self.createNew(0)
      }
   }

   // Method hasFlag() for compatibility with C#
   func hasFlag<T : EnumBitFlags>(_ flagToTest : T) -> Bool {
      return (self.rawValue & flagToTest.rawValue) != 0
   }
}

これはどのように使用できるかを示しています。

class TestEnumBitFlags {

   // Flags-style enum specifying where to write the log messages
   public enum LogDestination : UInt, EnumBitFlags {
      case none = 0             // Error condition
      case systemOutput = 0b01  // Logging messages written to system output file
      case sdCard       = 0b10  // Logging messages written to SD card (or similar storage)
      case both         = 0b11  // Both of the above options

      // Implement EnumBitFlags protocol
      public static func createNew(_ rawValue : UInt) -> LogDestination {
         return LogDestination(rawValue: rawValue)!
      }
   }

   private var _logDestination : LogDestination = .none
   private var _anotherEnum : LogDestination = .none

   func doTest() {

      _logDestination = .systemOutput
      assert(_logDestination.hasFlag(LogDestination.systemOutput))
      assert(!_logDestination.hasFlag(LogDestination.sdCard))

      _anotherEnum = _logDestination
      assert(_logDestination == _anotherEnum)

      _logDestination = .systemOutput | .sdCard
      assert(_logDestination.hasFlag(LogDestination.systemOutput) &&
             _logDestination.hasFlag(LogDestination.sdCard))

      /* don't do this, it results in a crash
      _logDestination = _logDestination & ~.systemOutput
      assert(_logDestination == .sdCard)
      */

      _logDestination = .sdCard
      _logDestination |= .systemOutput
      assert(_logDestination == .both)
   }
}

改善のための提案を歓迎します。

編集:私は自分でこの手法をあきらめたので、明らかにそれを推奨することはできません。

大きな問題は、Swiftは、rawValueが定義された列挙値の1つに一致する必要があることを要求します。これは、フラグビットが2または3または4 Swiftを幸せにするための値。しかし、5ビット以上のフラグビットの場合は、まったくおかしくなります。

誰かがそれを便利だと思う場合、または多分それをしない方法の警告として、これを投稿しておきます。

この状況に対する私の現在の解決策は、プロトコルといくつかの拡張メソッドとともに、enumの代わりに構造体を使用することに基づいています。これははるかに効果的です。たぶん私はそれがまた私に裏目に出ることはないだろうと確信しているときにそれを投稿するでしょう。

0
RenniePet

これは私のために働いた。

  • 1 << 0 // 0000
  • 1 << 1 // 0010
  • 1 << 2 // 0100
  • 1 << 3 // 1000

      enum Collision: Int {
        case Enemy, Projectile, Debris, Ground
        func bitmask() -> UInt32 {
            return 1 << self.rawValue
          }
      }
    
0
codebendr