C APIとの対話に関するAppleのドキュメントでは、NS_ENUM
マークの付いたCスタイルの列挙がSwift列挙としてインポートされる方法について説明しています。これは理にかなっており、Swiftの列挙はenum
値型として容易に提供されるため、独自の作成方法を簡単に確認できます。
さらに下に、NS_OPTIONS
マークの付いたCスタイルオプションについて次のように述べています。
Swiftは、
NS_OPTIONS
マクロでマークされたオプションもインポートします。オプションはインポートされた列挙と同様に動作しますが、オプションは&
、|
、~
などのビットごとの操作もサポートできます。 Objective-Cでは、空のオプションセットを定数ゼロ(0
)で表します。 Swiftでは、nil
を使用して、オプションがないことを表します。
Swiftにoptions
値型がない場合、Cスタイルオプション変数を作成して処理するにはどうすればよいですか?
Xcode 6.1 Beta 2では、RawOptionSetType
protocolにいくつかの変更が加えられました(これを参照してください Airspeedvelocityブログエントリ および Appleリリースノート )。
ネイトクックの例に基づいた更新されたソリューションです。次のように独自のオプションセットを定義できます。
struct MyOptions : RawOptionSetType, BooleanType {
private var value: UInt
init(_ rawValue: UInt) { self.value = rawValue }
// MARK: _RawOptionSetType
init(rawValue: UInt) { self.value = rawValue }
// MARK: NilLiteralConvertible
init(nilLiteral: ()) { self.value = 0}
// MARK: RawRepresentable
var rawValue: UInt { return self.value }
// MARK: BooleanType
var boolValue: Bool { return self.value != 0 }
// MARK: BitwiseOperationsType
static var allZeros: MyOptions { return self(0) }
// MARK: User defined bit values
static var None: MyOptions { return self(0) }
static var FirstOption: MyOptions { return self(1 << 0) }
static var SecondOption: MyOptions { return self(1 << 1) }
static var ThirdOption: MyOptions { return self(1 << 2) }
static var All: MyOptions { return self(0b111) }
}
次に、次のように使用して変数を定義できます。
let opt1 = MyOptions.FirstOption
let opt2:MyOptions = .SecondOption
let opt3 = MyOptions(4)
そして、このようにビットをテストします:
let singleOption = MyOptions.FirstOption
let multipleOptions: MyOptions = singleOption | .SecondOption
if multipleOptions & .SecondOption {
println("multipleOptions has SecondOption")
}
let allOptions = MyOptions.All
if allOptions & .ThirdOption {
println("allOptions has ThirdOption")
}
ドキュメントのSwift 2.0の例:
struct PackagingOptions : OptionSetType {
let rawValue: Int
init(rawValue: Int) { self.rawValue = rawValue }
static let Box = PackagingOptions(rawValue: 1)
static let Carton = PackagingOptions(rawValue: 2)
static let Bag = PackagingOptions(rawValue: 4)
static let Satchel = PackagingOptions(rawValue: 8)
static let BoxOrBag: PackagingOptions = [Box, Bag]
static let BoxOrCartonOrBag: PackagingOptions = [Box, Carton, Bag]
}
あなたはそれを見つけることができます こちら
Swift 2(現在はXcode 7ベータの一部としてベータ)では、NS_OPTIONS
スタイルのタイプは、新しい OptionSetType
タイプのサブタイプとしてインポートされます。そして、新しい Protocol Extensions 機能とOptionSetType
が標準ライブラリに実装される方法のおかげで、OptionsSetType
を拡張し、NS_OPTIONS
をインポートしたすべての同じ関数とメソッドを取得する独自の型を宣言できますスタイル型が取得します。
しかし、これらの関数はビット単位の算術演算子に基づいていません。 Cの一連の非排他的なブール型オプションを使用するには、フィールドのビットをマスクして調整する必要があるため、実装の詳細です。本当に、オプションのセットはset...一意のアイテムのコレクションです。したがって、OptionsSetType
は、配列リテラル構文からの作成、SetAlgebraType
などのクエリ、contains
によるマスキングなど、 intersection
プロトコルからすべてのメソッドを取得します(どのメンバーシップテストにどの面白い文字を使用するかを覚える必要はありません) !)
//Swift 2.0
//create
struct Direction : OptionSetType {
let rawValue: Int
static let None = Direction(rawValue: 0)
static let Top = Direction(rawValue: 1 << 0)
static let Bottom = Direction(rawValue: 1 << 1)
static let Left = Direction(rawValue: 1 << 2)
static let Right = Direction(rawValue: 1 << 3)
}
//declare
var direction: Direction = Direction.None
//using
direction.insert(Direction.Right)
//check
if direction.contains(.Right) {
//`enter code here`
}
Objective-Cと相互運用する必要がなく、Swiftのビットマスクのsurfaceのセマンティクスだけが必要な場合は、単純な「ライブラリ「通常の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 を見つけることができます。
必要な機能が|
とオプションを組み合わせて、&
と組み合わせたオプションに特定のオプションが含まれているかどうかを確認する方法だけである場合、Nate Cookの答えに代わるものは次のようになります。
オプションprotocol
を作成し、|
および&
をオーバーロードします。
protocol OptionsProtocol {
var value: UInt { get }
init (_ value: UInt)
}
func | <T: OptionsProtocol>(left: T, right: T) -> T {
return T(left.value | right.value)
}
func & <T: OptionsProtocol>(left: T, right: T) -> Bool {
if right.value == 0 {
return left.value == 0
}
else {
return left.value & right.value == right.value
}
}
次のように、オプション構造体をより簡単に作成できます。
struct MyOptions: OptionsProtocol {
private(set) var value: UInt
init (_ val: UInt) {value = val}
static var None: MyOptions { return self(0) }
static var One: MyOptions { return self(1 << 0) }
static var Two: MyOptions { return self(1 << 1) }
static var Three: MyOptions { return self(1 << 2) }
}
次のように使用できます。
func myMethod(#options: MyOptions) {
if options & .One {
// Do something
}
}
myMethod(options: .One | .Three)
複合オプションを組み合わせることができるかどうか疑問に思っている他の人のために追加の例を投稿するだけです。できますし、古き良きビットフィールドに慣れているなら、それらはあなたが期待するように結合します:
struct State: OptionSetType {
let rawValue: Int
static let A = State(rawValue: 1 << 0)
static let B = State(rawValue: 1 << 1)
static let X = State(rawValue: 1 << 2)
static let AB:State = [.A, .B]
static let ABX:State = [.AB, .X] // Combine compound state with .X
}
let state: State = .ABX
state.contains(.A) // true
state.contains(.AB) // true
セット[.AB, .X]
を[.A, .B, .X]
にフラット化します(少なくとも意味的に):
print(state) // 0b111 as expected: "State(rawValue: 7)"
print(State.AB) // 0b11 as expected: "State(rawValue: 3)"
Ricksterが既に述べたように、Swift 2.0で OptionSetType を使用できます。 NS_OPTIONS型は、オプションのセットのようなインターフェースを提供するOptionSetType
プロトコルに準拠するものとしてインポートされます。
struct CoffeeManipulators : OptionSetType {
let rawValue: Int
static let Milk = CoffeeManipulators(rawValue: 1)
static let Sugar = CoffeeManipulators(rawValue: 2)
static let MilkAndSugar = [Milk, Sugar]
}
この作業方法を提供します。
struct Coffee {
let manipulators:[CoffeeManipulators]
// You can now simply check if an option is used with contains
func hasMilk() -> Bool {
return manipulators.contains(.Milk)
}
func hasManipulators() -> Bool {
return manipulators.count != 0
}
}
re:いくつかのオプションを持つオプションセットを使用したサンドボックスとブックマークの作成
let options:NSURL.BookmarkCreationOptions = [.withSecurityScope,.securityScopeAllowOnlyReadAccess]
let temp = try link.bookmarkData(options: options, includingResourceValuesForKeys: nil, relativeTo: nil)
作成のためにオプションを組み合わせる必要があるソリューション。すべてのオプションが相互に排他的ではない場合に便利です。
(1 << 0)
、(1 << 1)
、(1 << 15)
など、さらに悪いことに1
、2
、16384
など、または16進数のバリエーションを使用する場合に避けられないビット位置のハードコーディングを避けるために、最初にenum
でビットを定義できます。 、その後、列挙型にビット順序計算を行わせます:
// Bits
enum Options : UInt {
case firstOption
case secondOption
case thirdOption
}
// Byte
struct MyOptions : OptionSet {
let rawValue: UInt
static let firstOption = MyOptions(rawValue: 1 << Options.firstOption.rawValue)
static let secondOption = MyOptions(rawValue: 1 << Options.secondOption.rawValue)
static let thirdOption = MyOptions(rawValue: 1 << Options.thirdOption.rawValue)
}
他の誰もそれについて言及していませんでした-そして、私はいくつかのいじくり回しの後にそれについてちょっと失策しました-Swiftセットはかなりうまくいくようです。
ビットマスクが実際に何を表しているのかを考えると(おそらくベン図に?)、それは空のセットである可能性があります。
もちろん、第一原理から問題にアプローチする際、ビット演算子の利便性は失われますが、読みやすさを向上させる強力なセットベースのメソッドが得られます。
これが私の例です。
enum Toppings : String {
// Just strings 'cause there's no other way to get the raw name that I know of...
// Could be 1 << x too...
case Tomato = "tomato"
case Salami = "salami"
case Cheese = "cheese"
case Chicken = "chicken"
case Beef = "beef"
case Anchovies = "anchovies"
static let AllOptions: Set<Toppings> = [.Tomato, .Salami, .Cheese, .Chicken, .Anchovies, .Beef]
}
func checkPizza(toppings: Set<Toppings>) {
if toppings.contains(.Cheese) {
print("Possible dairy allergies?")
}
let meats: Set<Toppings> = [.Beef, .Chicken, .Salami]
if toppings.isDisjointWith(meats) {
print("Vego-safe!")
}
if toppings.intersect(meats).count > 1 {
print("Limit one meat, or 50¢ extra charge!")
}
if toppings == [Toppings.Cheese] {
print("A bit boring?")
}
}
checkPizza([.Tomato, .Cheese, .Chicken, .Beef])
checkPizza([.Cheese])
これは、Cスタイルのソリューションを適応しようとするのではなく、Swiftによく似た、問題に対する第一原理アプローチに由来するものだと感じているからです。
また、整数の生の値がまだメリットを示すこの異なるパラダイムに挑戦するいくつかのObj-Cのユースケースを聞きたいと思います。
以下を使用します。取得できる値、配列のインデックス付け用の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
さらに必要な場合は、計算されたプロパティを追加します。
enum MyEnum: Int {
case one
case two
case four
case eight
var value: UInt8 {
return UInt8(1 << self.rawValue)
}
var string: String {
switch self {
case .one:
return "one"
case .two:
return "two"
case .four:
return "four"
case .eight:
return "eight"
}
}
}
オプションセットタイプを使用します。Swift 3で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 express: ShippingOptions = [.nextDay, .secondDay]
static let all: ShippingOptions = [.express, .priority, .standard]
}
ネイトの答え は良いのですが、次のようにDIYにします:
struct MyOptions : OptionSetType {
let rawValue: Int
static let None = Element(rawValue: 0)
static let FirstOption = Element(rawValue: 1 << 0)
static let SecondOption = Element(rawValue: 1 << 1)
static let ThirdOption = Element(rawValue: 1 << 2)
}