In der Apple-Dokumentation zur Interaktion mit C-APIs wird beschrieben, wie NS_ENUM
- markierte Aufzählungen im C-Stil als Swift-Aufzählungen importiert werden. Dies ist sinnvoll, und da Aufzählungen in Swift schnell als Werttyp enum
bereitgestellt werden, ist es leicht zu sehen, wie man eigene erstellt.
Weiter unten heißt es über NS_OPTIONS
- markierte Optionen im C-Stil:
Swift importiert auch Optionen, die mit dem Makro
NS_OPTIONS
markiert sind. Wohingegen Optionen verhalten sich ähnlich wie importierte Aufzählungen, Optionen können auch Unterstützung einiger bitweiser Operationen wie&
,|
und~
. In Objective-C Sie repräsentieren einen leeren Optionssatz mit der Konstanten Null (0
). Im Swift, verwenden Sienil
, um das Fehlen von Optionen darzustellen.
Da es in Swift keinen Werttyp options
gibt, wie können wir eine C-Style-Optionsvariable erstellen, mit der Sie arbeiten können?
Fast identisch mit Swift 2.0. OptionSetType wurde in OptionSet umbenannt und die Aufzählungen werden nach Konvention in Kleinbuchstaben geschrieben.
struct MyOptions : OptionSet {
let rawValue: Int
static let firstOption = MyOptions(rawValue: 1 << 0)
static let secondOption = MyOptions(rawValue: 1 << 1)
static let thirdOption = MyOptions(rawValue: 1 << 2)
}
Anstatt eine none
-Option bereitzustellen, wird in Swift 3 einfach ein leeres Array-Literal verwendet:
let noOptions: MyOptions = []
Andere Verwendung:
let singleOption = MyOptions.firstOption
let multipleOptions: MyOptions = [.firstOption, .secondOption]
if multipleOptions.contains(.secondOption) {
print("multipleOptions has SecondOption")
}
let allOptions = MyOptions(rawValue: 7)
if allOptions.contains(.thirdOption) {
print("allOptions has ThirdOption")
}
In Swift 2.0 kümmern sich Protokollerweiterungen um den Großteil der Boilerplate, die jetzt als eine Struktur importiert werden, die OptionSetType
entspricht. (RawOptionSetType
ist seit Swift 2 Beta 2 verschwunden.) Die Deklaration ist viel einfacher:
struct MyOptions : OptionSetType {
let rawValue: Int
static let None = MyOptions(rawValue: 0)
static let FirstOption = MyOptions(rawValue: 1 << 0)
static let SecondOption = MyOptions(rawValue: 1 << 1)
static let ThirdOption = MyOptions(rawValue: 1 << 2)
}
Jetzt können wir set-basierte Semantik mit MyOptions
verwenden:
let singleOption = MyOptions.FirstOption
let multipleOptions: MyOptions = [.FirstOption, .SecondOption]
if multipleOptions.contains(.SecondOption) {
print("multipleOptions has SecondOption")
}
let allOptions = MyOptions(rawValue: 7)
if allOptions.contains(.ThirdOption) {
print("allOptions has ThirdOption")
}
Wenn Sie die Objective-C-Optionen betrachten, die von Swift importiert wurden (zum Beispiel UIViewAutoresizing
), können Sie feststellen, dass Optionen als struct
deklariert werden, die dem Protokoll RawOptionSetType
entspricht, das wiederum _RawOptionSetType
, Equatable
, RawRepresentable
, BitwiseOperationsType
und NilLiteralConvertible
entspricht. . Wir können unsere eigenen so erstellen:
struct MyOptions : 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: MyOptions { return self(0) }
static func fromMask(raw: UInt) -> MyOptions { return self(raw) }
var rawValue: UInt { return self.value }
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) }
}
Jetzt können wir diese neue Optionsgruppe MyOptions
genauso behandeln, wie in der Dokumentation von Apple beschrieben: Sie können enum
- ähnliche Syntax verwenden:
let opt1 = MyOptions.FirstOption
let opt2: MyOptions = .SecondOption
let opt3 = MyOptions(4)
Und es verhält sich auch so, als würden wir Optionen erwarten:
let singleOption = MyOptions.FirstOption
let multipleOptions: MyOptions = singleOption | .SecondOption
if multipleOptions & .SecondOption != nil { // see note
println("multipleOptions has SecondOption")
}
let allOptions = MyOptions.fromMask(7) // aka .fromMask(0b111)
if allOptions & .ThirdOption != nil {
println("allOptions has ThirdOption")
}
Ich habe einen Generator gebaut, um einen Swift-Optionssatz zu erstellen ohne all das Suchen/Ersetzen.
Latest: Änderungen für Swift 1.1 Beta 3.
Xcode 6.1 Beta 2 hat einige Änderungen am RawOptionSetType
-Protokoll vorgenommen (siehe diesen Airspeedvelocity-Blogeintrag und die Apple-Versionshinweise ).
Basierend auf Nate Cooks Beispiel ist hier eine aktualisierte Lösung. Sie können Ihr eigenes Optionsset folgendermaßen definieren:
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) }
}
Es kann dann so verwendet werden, um Variablen zu definieren:
let opt1 = MyOptions.FirstOption
let opt2:MyOptions = .SecondOption
let opt3 = MyOptions(4)
Und so zum Testen auf Bits:
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-Beispiel aus der Dokumentation:
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]
}
Sie finden es hier
In Swift 2 (derzeit Beta als Teil der Xcode 7-Betaversion) werden NS_OPTIONS
- Stil-Typen als Untertypen des neuen Typs OptionSetType
importiert. Und dank der neuen Protocol Extensions -Funktion und der Art und Weise, wie OptionSetType
in der Standardbibliothek implementiert ist, können Sie Ihre eigenen Typen deklarieren, die OptionsSetType
erweitern, und alle Funktionen und Methoden erhalten, die importierte NS_OPTIONS
- Stiltypen erhalten.
Diese Funktionen basieren jedoch nicht mehr auf bitweisen arithmetischen Operatoren. Das Arbeiten mit einer Reihe nicht exklusiver boolescher Optionen in C erfordert das Maskieren und Verwischen von Bits in einem Feld ist ein Implementierungsdetail. Wirklich ist eine Menge von Optionen eine Menge ... eine Sammlung von einzigartigen Elementen. So bekommt OptionsSetType
alle Methoden aus dem SetAlgebraType
Protokoll, wie die Erstellung aus der Array-Literal-Syntax, Abfragen wie contains
, Maskieren mit intersection
usw. (Sie müssen sich nicht mehr daran erinnern, welcher lustige Charakter für welchen Mitgliedschaftstest verwendet werden muss!).
//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`
}
Wenn Sie nicht mit Objective-C zusammenarbeiten müssen und nur die surface-Semantik von Bitmasken in Swift benötigen, habe ich eine einfache "Bibliothek" namens BitwiseOptions geschrieben, die dies mit regulären Swift-Enumerationen tun kann, z.
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!")
}
und so weiter. Hier werden keine echten Bits umgedreht. Dies sind Operationen, die auf undurchsichtige Werte eingestellt sind. Sie finden den Gist hier .
Wenn die einzige Funktionalität, die wir benötigen, eine Möglichkeit ist, Optionen mit |
zu kombinieren und zu prüfen, ob kombinierte Optionen eine bestimmte Option mit &
enthalten, könnte dies eine Alternative zu Nate Cooks Antwort sein:
Erstellen Sie eine Option protocol
und eine Überladung |
und &
:
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
}
}
Jetzt können wir Optionsstrukturen einfacher so erstellen:
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) }
}
Sie können wie folgt verwendet werden:
func myMethod(#options: MyOptions) {
if options & .One {
// Do something
}
}
myMethod(options: .One | .Three)
Posten Sie einfach ein zusätzliches Beispiel für alle, die sich gefragt haben, ob Sie zusammengesetzte Optionen kombinieren können. Sie können und sie kombinieren, wie Sie es erwarten würden, wenn Sie an gute alte Bitfelder gewöhnt sind:
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
Es reduziert die Menge [.AB, .X]
in [.A, .B, .X]
(zumindest semantisch):
print(state) // 0b111 as expected: "State(rawValue: 7)"
print(State.AB) // 0b11 as expected: "State(rawValue: 3)"
Wie Rickster bereits erwähnt hat, können Sie OptionSetType in Swift 2.0 verwenden. NS_OPTIONS-Typen werden importiert, da sie dem OptionSetType
-Protokoll entsprechen, das eine satzähnliche Schnittstelle für Optionen bietet:
struct CoffeeManipulators : OptionSetType {
let rawValue: Int
static let Milk = CoffeeManipulators(rawValue: 1)
static let Sugar = CoffeeManipulators(rawValue: 2)
static let MilkAndSugar = [Milk, Sugar]
}
Es gibt Ihnen diese Art zu arbeiten:
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: Sandbox- und Lesezeichenerstellung mit Optionssätzen mit mehreren Optionen
let options:NSURL.BookmarkCreationOptions = [.withSecurityScope,.securityScopeAllowOnlyReadAccess]
let temp = try link.bookmarkData(options: options, includingResourceValuesForKeys: nil, relativeTo: nil)
lösung für die Kombination von Optionen für Kreationen, nützlich, wenn sich nicht alle Optionen gegenseitig ausschließen.
Um eine harte Kodierung der Bitpositionen zu vermeiden, die bei der Verwendung von (1 << 0)
, (1 << 1)
, (1 << 15)
usw. oder noch schlechter 1
, 2
, 16384
usw. oder einer hexadezimalen Variation unvermeidbar ist, könnte man zuerst die Bits in einer enum
definieren und diese dann angeben enum tun die bit ordinal berechnung:
// 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)
}
Niemand sonst hat es erwähnt - und nach einigem Basteln bin ich irgendwie darauf gestoßen -, aber ein Swift-Set scheint ziemlich gut zu funktionieren.
Wenn wir (vielleicht an ein Venn-Diagramm?) Darüber nachdenken, was eine Bitmaske tatsächlich darstellt, ist es eine möglicherweise leere Menge.
Wenn wir uns dem Problem von den ersten Prinzipien aus nähern, verlieren wir natürlich die Bequemlichkeit bitweiser Operatoren, gewinnen aber leistungsfähige satzbasierte Methoden, die die Lesbarkeit verbessern.
Hier ist mein Basteln zum Beispiel:
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])
Ich finde das schön, weil ich der Meinung bin, es komme von einem ersten Prinzipansatz des Problems - ähnlich wie von Swift -, anstatt zu versuchen, C-artige Lösungen anzupassen.
Ich möchte auch gerne einige Anwendungsfälle von Obj-C hören, die dieses andere Paradigma in Frage stellen, bei dem die ganzzahligen Rohwerte immer noch Vorteile aufweisen.
Ich verwende Folgendes: Ich brauche die beiden Werte, die ich erhalten kann, rawValue für die Indexierung von Arrays und den Wert für Flags.
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
Und wenn Sie mehr brauchen, fügen Sie einfach eine berechnete Eigenschaft hinzu.
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"
}
}
}
Verwenden Sie einen Optionssatztyp, in 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]
}
Nates Antwort ist gut, aber ich würde es gerne selbst machen:
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)
}