ios - nil - swift 4 operators
¿Cómo crear las enumeraciones de máscara de bits de estilo NS_OPTIONS en Swift? (15)
Swift 3.0
Casi idéntico a Swift 2.0. OptionSetType se renombró a OptionSet y las enumeraciones se escriben minúsculas por convención.
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)
}
En lugar de proporcionar una opción none
, la recomendación de Swift 3 es simplemente usar un literal vacío de matriz:
let noOptions: MyOption = []
Otro uso:
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
En Swift 2.0, las extensiones de protocolo se encargan de la mayoría de las repeticiones estándar para estos, que ahora se importan como una estructura que se ajusta a OptionSetType
. ( RawOptionSetType
ha desaparecido a partir de Swift 2 beta 2.) La declaración es mucho más simple:
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)
}
Ahora podemos usar la semántica basada en conjuntos con 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
Al observar las opciones de Objective-C que Swift ( UIViewAutoresizing
, por ejemplo) importó, podemos ver que las opciones se declaran como una struct
que se ajusta al protocolo RawOptionSetType
, que a su vez se ajusta a _RawOptionSetType
, Equatable
, RawRepresentable
, BitwiseOperationsType
y NilLiteralConvertible
. Podemos crear el nuestro así:
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) }
}
Ahora podemos tratar este nuevo conjunto de opciones, MyOptions
, tal como se describe en la documentación de Apple: puede usar la sintaxis de tipo enum
:
let opt1 = MyOptions.FirstOption
let opt2: MyOptions = .SecondOption
let opt3 = MyOptions(4)
Y también se comporta como esperamos que se comporten las opciones:
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")
}
Construí un generador para crear un conjunto de opciones Swift sin todo el hallazgo / reemplazo.
Último: Modificaciones para Swift 1.1 beta 3.
En la documentación de Apple sobre la interacción con las API de C, describen la forma en que se NS_ENUM
enumeraciones de estilo C con la NS_ENUM
NS_ENUM como empadronamientos Swift. Esto tiene sentido, y como las enumeraciones en Swift se proporcionan fácilmente como el tipo de valor enum
, es fácil ver cómo crear las nuestras.
Más abajo, dice esto sobre NS_OPTIONS
-marked C-style options:
Swift también importa las opciones marcadas con la macro
NS_OPTIONS
. Mientras que las opciones se comportan de manera similar a las enumeraciones importadas, las opciones también pueden admitir algunas operaciones a nivel de bit, como&
,|
y~
. En Objective-C, representa una opción vacía establecida con el cero constante (0
). En Swift, usenil
para representar la ausencia de cualquier opción.
Dado que no hay un tipo de valor de options
en Swift, ¿cómo podemos crear una variable de opciones de estilo C para trabajar?
Como Rickster ya mencionó, puedes usar OptionSetType en Swift 2.0. Los tipos NS_OPTIONS se importan conforme al protocolo OptionSetType
, que presenta una interfaz tipo conjunto para las opciones:
struct CoffeeManipulators : OptionSetType {
let rawValue: Int
static let Milk = CoffeeManipulators(rawValue: 1)
static let Sugar = CoffeeManipulators(rawValue: 2)
static let MilkAndSugar = [Milk, Sugar]
}
Te da esta forma de trabajar:
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
}
}
Ejemplo de Swift 2.0 de la documentación:
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]
}
Puedes encontrarlo here
En Swift 2 (actualmente beta como parte de Xcode 7 beta), los tipos de estilo NS_OPTIONS
se importan como subtipos del nuevo tipo OptionSetType
. Y gracias a la nueva característica Extensiones de protocolo y la forma en que se implementa OptionSetType
en la biblioteca estándar, puede declarar sus propios tipos que amplían OptionsSetType
y obtienen todas las mismas funciones y métodos que NS_OPTIONS
tipos importados de estilo NS_OPTIONS
.
Pero esas funciones ya no se basan en operadores aritméticos bit a bit. Que trabajar con un conjunto de opciones booleanas no exclusivas en C requiere enmascaramiento y bifurcar bits en un campo es un detalle de implementación. Realmente, un conjunto de opciones es un conjunto ... una colección de artículos únicos. Así que OptionsSetType
obtiene todos los métodos del protocolo SetAlgebraType
, como la creación a partir de la sintaxis literal de la matriz, consultas como contains
, enmascaramiento con intersection
, etc. (¡Ya no tendrá que recordar qué personaje divertido usar para qué prueba de membresía!)
Nadie más lo ha mencionado, y yo cometí un error después de algunos retoques, pero un Swift Set parece funcionar bastante bien.
Si pensamos (¿quizás en un diagrama de Venn?) Qué es lo que realmente representa una máscara de bits, es un conjunto posiblemente vacío.
Por supuesto, al abordar el problema desde los primeros principios, perdemos la comodidad de los operadores bit a bit, pero obtenemos métodos potentes basados en conjuntos que mejoran la legibilidad.
Aquí está mi retoque, por ejemplo:
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])
Me parece agradable porque creo que proviene de un primer enfoque de principio al problema, al igual que Swift, en lugar de tratar de adaptar las soluciones de estilo C.
También me gustaría escuchar algunos casos de uso de Obj-C que desafiarían este paradigma diferente, donde los valores en bruto enteros aún muestran mérito.
Para evitar la codificación dura de las posiciones de bit, que es inevitable cuando se usan (1 << 0)
, (1 << 1)
, (1 << 15)
etc. o incluso peor 1
, 2
, 16384
etc. o algunos hexadecimales variación, uno podría primero definir los bits en una enum
, luego dejar que dicha enumeración haga el cálculo ordinal de bits:
// 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)
}
Si la única funcionalidad que necesitamos es una forma de combinar opciones con |
y compruebe si las opciones combinadas contienen una opción particular con &
una alternativa a la respuesta de Nate Cook podría ser esta:
Crear un protocol
opciones y sobrecargar |
y &
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
}
}
Ahora podemos crear estructuras de opciones de forma más sencilla como sigue:
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) }
}
Se pueden usar de la siguiente manera:
func myMethod(#options: MyOptions) {
if options & .One {
// Do something
}
}
myMethod(options: .One | .Three)
Si no necesita interoperar con Objective-C y solo desea la semántica de la superficie de las máscaras de bits en Swift, he escrito una simple "biblioteca" llamada BitwiseOptions que puede hacer esto con enumeraciones regulares de Swift, por ejemplo:
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!")
}
y así. No se están volteando bits reales aquí. Estas son operaciones establecidas en valores opacos. Puedes encontrar la esencia here .
Solo publicando un ejemplo extra para cualquier otra persona que se preguntaba si podría combinar opciones compuestas. Puedes, y se combinan como esperarías si estás acostumbrado a buenos campos de bit viejos:
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
Aplana el conjunto [.AB, .X]
en [.A, .B, .X]
(al menos semánticamente):
print(state) // 0b111 as expected: "State(rawValue: 7)"
print(State.AB) // 0b11 as expected: "State(rawValue: 3)"
Uso lo siguiente Necesito los dos valores que puedo obtener, rawValue para indexar matrices y valor para 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
Y si uno necesita más simplemente agregue una propiedad calculada.
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"
}
}
}
Utilice un tipo de conjunto de opciones, en el uso rápido OptionSet
3
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]
}
Xcode 6.1 Beta 2 trajo algunos cambios al protocolo RawOptionSetType
(vea esta entrada del blog Airspeedvelocity y las notas de la versión de Apple ).
Basado en el ejemplo de Nate Cooks aquí hay una solución actualizada. Puede definir su propia opción establecida de esta manera:
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) }
}
Se puede usar así para definir variables:
let opt1 = MyOptions.FirstOption
let opt2:MyOptions = .SecondOption
let opt3 = MyOptions(4)
Y así para probar 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")
}
re: Creaciones de sandbox y marcadores usando conjuntos de opciones con varias opciones
let options:NSURL.BookmarkCreationOptions = [.withSecurityScope,.securityScopeAllowOnlyReadAccess]
let temp = try link.bookmarkData(options: options, includingResourceValuesForKeys: nil, relativeTo: nil)
solución a la necesidad de combinar opciones para creaciones, útil cuando no todas las opciones son mutuamente excluyentes.
La respuesta de Nate es buena, pero yo la haría bricolaje, así:
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)
}
//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`
}