140 votes

Comment créer des énumérations bitmask de type NS_OPTIONS en Swift ?

Dans la documentation d'Apple sur l'interaction avec les API en C, ils décrivent la manière suivante NS_ENUM -Les énumérations de style C marquées sont importées comme des énumérations Swift. C'est logique, et puisque les énumérations en Swift sont facilement fournies par la fonction enum il est facile de voir comment créer notre propre type de valeur.

Plus bas, il est dit ceci à propos de NS_OPTIONS -Options de style C marquées :

Swift importe également les options marquées du symbole NS_OPTIONS macro. Alors que options se comportent de manière similaire aux énumérations importées, les options peuvent aussi supporter certaines opérations binaires, telles que & , | y ~ . En Objective-C, on représente un ensemble d'options vide par la constante zéro ( 0 ). Dans Swift, utilisez nil pour représenter l'absence de toute option.

Étant donné qu'il n'y a pas de options en Swift, comment créer une variable d'options de type C pour travailler avec ?

3 votes

Le très célèbre "NSHipster" de @Mattt contient une description détaillée de l'appareil. RawOptionsSetType : nshipster.com/rawoptionsettype

0 votes

264voto

Nate Cook Points 4815

Swift 3.0

Presque identique à Swift 2.0. OptionSetType a été renommé OptionSet et les enums sont écrits 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 un none la recommandation de Swift 3 est d'utiliser simplement un littéral de tableau vide :

let noOptions: MyOptions = []

Autre usage :

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

Dans Swift 2.0, les extensions de protocole prennent en charge la plupart des éléments standard de ces derniers, qui sont désormais importés sous la forme d'une structure conforme aux normes suivantes OptionSetType . ( RawOptionSetType a disparu à partir de 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)
}

Maintenant, nous pouvons 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")
}

Swift 1.2

En regardant les options Objective-C qui ont été importées par Swift ( UIViewAutoresizing par exemple), on peut voir que les options sont déclarées en tant que struct qui se conforme au protocole RawOptionSetType qui, à son tour, est conforme à _RawOptionSetType , Equatable , RawRepresentable , BitwiseOperationsType y NilLiteralConvertible . Nous pouvons créer le nôtre 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 fonction enum -comme une syntaxe :

let opt1 = MyOptions.FirstOption
let opt2: MyOptions = .SecondOption
let opt3 = MyOptions(4)

Et il se comporte également comme nous nous attendons à 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 pour créer un jeu d'options Swift sans toutes les recherches/remplacements.

Le plus récent : Modifications pour Swift 1.1 beta 3.

1 votes

Ça n'a pas marché pour moi à moins que je fasse value a UInt32 . Vous n'avez pas non plus besoin de définir les fonctions, car les fonctions pertinentes sont déjà définies pour les éléments suivants RawOptionSet (par exemple func |<T : RawOptionSet>(a: T, b: T) -> T )

0 votes

Merci, bonne remarque sur les fonctions - je pense que le compilateur se plaignait de celles-ci alors que le reste de la conformité au protocole n'était pas en place. Quels problèmes avez-vous rencontrés avec UInt ? Cela fonctionne bien pour moi.

0 votes

En l'utilisant dans une aire de jeu avec UInt, j'ai obtenu "fatal error : Can't unwrap Optional.None", mais cela a bien fonctionné avec UInt32. Vraiment bizarre. Quoi qu'il en soit, j'ai réussi à utiliser des classes pour prendre en charge toutes les fonctions requises pour une implémentation légèrement plus propre : davidlawson.com.au/2014/06/implementation de ns_options-en-swift

12voto

Klaas Points 2995

Xcode 6.1 Beta 2 a apporté quelques changements à la RawOptionSetType (voir ce Entrée du blog Airspeedvelocity et le Notes de mise à jour d'Apple ).

Sur la base de l'exemple de Nate Cooks, voici une solution actualisée. 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 les 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")
}

8voto

Tomasz Bąk Points 405

Exemple de Swift 2.0 tiré 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 aquí

6voto

rickster Points 19870

En Swift 2 (actuellement en bêta dans le cadre de la bêta de Xcode 7), NS_OPTIONS sont importés en tant que sous-types du nouveau modèle de type OptionSetType type. Et grâce à la nouvelle Extensions du protocole et la façon dont OptionSetType est implémentée dans la bibliothèque standard, vous pouvez déclarer vos propres types qui étendent la fonction OptionsSetType et obtenir toutes les mêmes fonctions et méthodes que celles importées. NS_OPTIONS -Les types de style sont obtenus.

Mais ces fonctions ne sont plus basées sur des opérateurs arithmétiques de type bit à bit. Le fait que travailler avec un ensemble d'options booléennes non exclusives en C nécessite de masquer et de manipuler des bits dans un champ est un détail d'implémentation. En réalité, un ensemble d'options est un set ... une collection d'objets uniques. Donc OptionsSetType récupère toutes les méthodes de la SetAlgebraType comme la création d'un tableau à partir de la syntaxe littérale, des requêtes telles que contains le masquage avec intersection (Vous n'aurez plus à vous rappeler quel personnage drôle utiliser pour quel test d'adhésion).

5voto

PhuocLuong Points 94
//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`
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X