Dans la documentation d'Apple sur l'interaction avec les API C, ils décrivent la manière dont les énumérations de style C marquées par NS_ENUM
- sont marquées en tant que énumérations Swift. Cela a du sens, et comme les énumérations dans Swift sont facilement fournies en tant que type de valeur enum
, il est facile de voir comment créer la nôtre.
Plus bas, il est dit ceci à propos des options de style C marquées par NS_OPTIONS
-:
Swift importe également les options marquées avec la macro
NS_OPTIONS
. Tandis que les options se comportent de la même manière que les énumérations importées, les options peuvent aussi prend en charge certaines opérations au niveau des bits, telles que&
,|
et~
. En Objective-C, vous représentez un jeu d'options vide avec la constante zéro (0
). Dans Swift, utiliseznil
pour représenter l'absence de toute option.
Étant donné qu’il n’ya pas de type de valeur options
dans Swift, comment pouvons-nous créer une variable d’options C-Style avec laquelle travailler?
Presque identique à Swift 2.0. OptionSetType a été renommé OptionSet et les énumérations sont écrites en minuscules par convention.
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)
}
Au lieu de fournir une option none
, la recommandation de Swift 3 consiste simplement à utiliser un littéral de tableau vide:
let noOptions: MyOptions = []
Autre utilisation:
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")
}
Dans Swift 2.0, les extensions de protocole s’occupent de la plupart des règles d’application, qui sont maintenant importées en tant que structure conforme à OptionSetType
. (RawOptionSetType
a disparu depuis Swift 2 beta 2.) La déclaration est beaucoup plus 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)
}
Nous pouvons maintenant utiliser la sémantique basée sur les ensembles avec 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")
}
En examinant les options Objective-C importées par Swift (UIViewAutoresizing
, par exemple), nous pouvons voir que les options sont déclarées en tant que struct
conforme au protocole RawOptionSetType
, lequel est lui aussi conforme à _RawOptionSetType
, Equatable
, RawRepresentable
, BitwiseOperationsType
et NilLiteralConvertible
. Nous pouvons créer notre propre comme ceci:
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) }
}
Nous pouvons maintenant traiter ce nouvel ensemble d'options, MyOptions
, comme décrit dans la documentation d'Apple: vous pouvez utiliser la syntaxe enum
- like:
let opt1 = MyOptions.FirstOption
let opt2: MyOptions = .SecondOption
let opt3 = MyOptions(4)
Et il se comporte également comme nous nous attendions à ce que les options se comportent:
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")
}
J'ai construit un générateur pour créer un jeu d'options Swift } sans toute la recherche/remplacement.
Dernières: Modifications pour Swift 1.1 beta 3.
Xcode 6.1 bêta 2 a apporté quelques modifications au protocole RawOptionSetType
(voir cette entrée de blog Airspeedvelocity et les notes de publication de Apple ).
Basé sur l'exemple de Nate Cooks, voici une solution mise à jour. Vous pouvez définir votre propre jeu d’options comme ceci:
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) }
}
Il peut ensuite être utilisé comme ceci pour définir des variables:
let opt1 = MyOptions.FirstOption
let opt2:MyOptions = .SecondOption
let opt3 = MyOptions(4)
Et comme ça pour tester des 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 exemple de la documentation:
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]
}
Vous pouvez le trouver ici
Dans Swift 2 (version bêta actuellement intégrée à la version bêta de Xcode 7), les types de style NS_OPTIONS
- sont importés en tant que sous-types du nouveau type OptionSetType
. Et grâce à la nouvelle fonctionnalité Protocol Extensions et à la façon dont OptionSetType
est implémenté dans la bibliothèque standard, vous pouvez déclarer vos propres types qui étendent OptionsSetType
et obtenir toutes les mêmes fonctions et méthodes que celles importées par les types de style NS_OPTIONS
-.
Mais ces fonctions ne sont plus basées sur des opérateurs arithmétiques au niveau des bits. Le fait de travailler avec un ensemble d’options booléennes non exclusives en C nécessite le masquage et le twiddling de bits dans un champ est un détail d’implémentation. Vraiment, un ensemble d’options est un set ... un ensemble d’éléments uniques. Donc, OptionsSetType
obtient toutes les méthodes du protocole SetAlgebraType
, comme la création à partir de la syntaxe littérale d'un tableau, des requêtes telles que contains
, le masquage avec intersection
, etc.
//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`
}
Si vous n'avez pas besoin d'interagir avec Objective-C et que vous voulez juste la sémantique de surface des masques de bits dans Swift, j'ai écrit une simple "bibliothèque" appelée BitwiseOptions qui peut le faire avec des énumérations Swift régulières par exemple:
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!")
}
etc. Aucun bit réel n'est retourné ici. Ce sont des opérations définies sur des valeurs opaques. Vous pouvez trouver le Gist ici .
Si la seule fonctionnalité dont nous avons besoin est un moyen de combiner des options avec |
et de vérifier si les options combinées contiennent une option particulière avec &
, une alternative à la réponse de Nate Cook pourrait être:
Créez une option protocol
et une surcharge |
et &
:
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
}
}
Maintenant, nous pouvons créer des options plus simplement comme ceci:
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) }
}
Ils peuvent être utilisés comme suit:
func myMethod(#options: MyOptions) {
if options & .One {
// Do something
}
}
myMethod(options: .One | .Three)
Il suffit de poster un exemple supplémentaire pour quiconque se demande si vous pouvez combiner des options composées. Vous pouvez, et ils se combinent comme vous vous en doutez si vous avez l'habitude des bons vieux champs de bits:
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
Il aplatit l'ensemble [.AB, .X]
en [.A, .B, .X]
(au moins sémantiquement):
print(state) // 0b111 as expected: "State(rawValue: 7)"
print(State.AB) // 0b11 as expected: "State(rawValue: 3)"
Comme Rickster l'a déjà mentionné, vous pouvez utiliser OptionSetType dans Swift 2.0. Les types NS_OPTIONS sont importés conformément au protocole OptionSetType
, qui présente une interface de type ensemble pour les options:
struct CoffeeManipulators : OptionSetType {
let rawValue: Int
static let Milk = CoffeeManipulators(rawValue: 1)
static let Sugar = CoffeeManipulators(rawValue: 2)
static let MilkAndSugar = [Milk, Sugar]
}
Cela vous donne cette façon de travailler:
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: créations Sandbox et signets en utilisant des jeux d'options avec plusieurs options
let options:NSURL.BookmarkCreationOptions = [.withSecurityScope,.securityScopeAllowOnlyReadAccess]
let temp = try link.bookmarkData(options: options, includingResourceValuesForKeys: nil, relativeTo: nil)
solution pour avoir besoin de combiner les options pour les créations, utile lorsque toutes les options ne sont pas mutuellement exclusives.
Personne d'autre n'en a parlé - et j'ai un peu gaffé après quelques travaux de bricolage - mais un Swift Set semble fonctionner assez bien.
Si nous pensons (peut-être à un diagramme de Venn?) À ce qu'un masque de bits représente réellement, c'est un ensemble éventuellement vide.
Bien sûr, en abordant le problème à partir des premiers principes, nous perdons la commodité des opérateurs au niveau des bits, mais nous obtenons de puissantes méthodes basées sur des ensembles qui améliorent la lisibilité.
Voici mon bricolage par exemple:
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])
Je trouve cela agréable parce que j’ai le sentiment que cela découle d’une approche fondée sur les principes de base, semblable à celle de Swift, plutôt que d’essayer d’adapter des solutions de type C.
Voudrait aussi entendre certains cas d’utilisation Obj-C qui remettraient en cause ce paradigme différent, où les valeurs entières brutes sont toujours valables.
J'utilise ce qui suit, j'ai besoin des deux valeurs que je peux obtenir, rawValue pour les tableaux d'indexation et valeur pour les indicateurs.
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
Et si l’on a besoin de plus, il suffit d’ajouter une propriété calculée.
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"
}
}
}
Utilisez un type de jeu d'options, dans Swift 3, utilisez 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]
}
La réponse de Nate est bonne mais je le ferais bricolage
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)
}