В документации Apple о взаимодействии с C API описывается, как NS_ENUM
отмеченные обозначения перечисления в стиле C импортируются как перечисления Swift. В этом есть смысл, и поскольку перечисления в Swift легко предоставляются как enum
тип значения, легко увидеть, как создать свои собственные.
NS_OPTIONS
Ниже говорится об опциях C-стиля с пометкой:
Swift также импортирует параметры, отмеченные
NS_OPTIONS
макросом. В то время как варианты ведут себя так же , как импортируемые перечисления, варианты могут также поддерживать некоторые битовые операции, такие как&
,|
, и~
. В Objective-C вы представляете пустой набор параметров с константой ноль (0
). В Swift используйтеnil
для обозначения отсутствия каких-либо параметров.
Учитывая, что options
в Swift нет типа значения, как мы можем создать переменную параметров C-Style для работы?
источник
RawOptionsSetType
: nshipster.com/rawoptionsettypeОтветы:
Swift 3.0
Практически идентичен Swift 2.0. OptionSetType был переименован в OptionSet, и перечисления по соглашению записываются в нижнем регистре.
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) }
Вместо того, чтобы предоставлять
none
вариант, Swift 3 рекомендует просто использовать пустой литерал массива:let noOptions: MyOptions = []
Другое использование:
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") }
Swift 2.0
В Swift 2.0 расширения протокола берут на себя большую часть шаблонов для них, которые теперь импортируются как структура, соответствующая
OptionSetType
. (RawOptionSetType
исчез в Swift 2 beta 2.) Объявление намного проще: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) }
Теперь мы можем использовать семантику на основе наборов с
MyOptions
: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") }
Swift 1.2
Глядя на варианты Objective-C , которые были импортированы Свифт (
UIViewAutoresizing
например), мы можем видеть , что варианты объявлены в качестве ,struct
которое соответствует протоколуRawOptionSetType
, который , в свою очередь , соответствует требованиям_RawOptionSetType
,Equatable
,RawRepresentable
,BitwiseOperationsType
, иNilLiteralConvertible
. Мы можем создать свой вот так: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) } }
Теперь мы можем обрабатывать этот новый набор параметров,
MyOptions
как описано в документации Apple: вы можете использоватьenum
синтаксис -like:let opt1 = MyOptions.FirstOption let opt2: MyOptions = .SecondOption let opt3 = MyOptions(4)
И он также ведет себя так, как мы ожидаем от опций:
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") }
Я построил генератор для создания набора опций Swift без необходимости поиска / замены.
Последнее: Модификации для Swift 1.1 beta 3.
источник
value
UInt32
RawOptionSet
func |<T : RawOptionSet>(a: T, b: T) -> T
UInt
? У меня все работает нормально.enum CollisionTypes: UInt32 { case Player = 1 case Wall = 2 case Star = 4 case Vortex = 8 case Finish = 16 }
Xcode 6.1 Beta 2 внес некоторые изменения в
RawOptionSetType
протокол (см. Эту запись в блоге 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
типы -style импортируются как подтипы новогоOptionSetType
типа. А благодаря новой функции Protocol Extensions и способуOptionSetType
, реализованному в стандартной библиотеке, вы можете объявлять свои собственные типы, которые расширяютсяOptionsSetType
и получают все те же функции и методы, что и импортированныеNS_OPTIONS
типы -style.Но эти функции больше не основаны на побитовых арифметических операторах. То, что для работы с набором неисключительных логических опций в C требуется маскирование и подстановка битов в поле, является деталью реализации. На самом деле набор опций - это набор ... набор уникальных предметов. Таким образом,
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, я написал простую «библиотеку» под названием BitwiseOptions, которая может делать это с помощью обычных перечислений Swift, например:
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!") }
и так далее. Никакие фактические биты здесь не переворачиваются. Это операции установки над непрозрачными значениями. Вы можете найти суть здесь .
источник
Как уже упоминал Рикстер, вы можете использовать OptionSetType в Swift 2.0. Типы 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 } }
источник
Если единственная функция, которая нам нужна, - это способ комбинировать параметры
|
и проверять, содержат ли комбинированные параметры конкретную опцию, с&
альтернативой Нейту Куку, ответ может быть следующим:Создайте параметры
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)"
источник
Никто больше об этом не упомянул - и я вроде как наткнулся на это после некоторой работы - но Swift Set, похоже, работает довольно хорошо.
Если мы подумаем (может быть, на диаграмме Венна?) О том, что на самом деле представляет битовая маска, это, возможно, пустой набор.
Конечно, подходя к проблеме из первых принципов, мы теряем удобство поразрядных операторов, но получаем мощные методы, основанные на наборах, которые улучшают читаемость.
Вот, например, моя работа:
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])
Мне это нравится, потому что я чувствую, что это исходит из подхода к проблеме из первых принципов - во многом как в Swift - вместо того, чтобы пытаться адаптировать решения в стиле C.
Также хотелось бы услышать некоторые варианты использования Obj-C, которые бросают вызов этой другой парадигме, где целые необработанные значения по-прежнему показывают достоинства.
источник
Для того , чтобы избежать жесткого кодирования позиции бит, которая является неизбежной при использовании
(1 << 0)
,(1 << 1)
, и(1 << 15)
т.д. , или даже хуже1
,2
, и16384
т.д. , или некоторых вариаций шестнадцатеричных, можно первым определяют биты в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) }
источник
Я использую следующее: мне нужны оба значения, которые я могу получить: 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" } } }
источник
re: Создание песочницы и закладок с использованием наборов опций с несколькими опциями
let options:NSURL.BookmarkCreationOptions = [.withSecurityScope,.securityScopeAllowOnlyReadAccess] let temp = try link.bookmarkData(options: options, includingResourceValuesForKeys: nil, relativeTo: nil)
решение необходимости комбинировать варианты создания, полезно, когда не все варианты являются взаимоисключающими.
источник
Ответ Нейта хорош, но я бы сделал его своими руками, вот так:
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) }
источник
Используйте тип набора параметров, в быстром 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] }
источник