Почему мы не можем установить последовательный rawValue на Option Sets?

2 Abhishek Dave [2018-02-02 15:07:00]

Сегодня я попробовал сыграть бит с OptionSet в Playground, и я заметил этот шаблон

 struct Activities: OptionSet {
      let rawValue: Int
      static let eating = Activities(rawValue: 1)
      static let programming = Activities(rawValue: 2)
      static let breathing = Activities(rawValue: 3)
      static let saveGotham = Activities(rawValue: 4) 
}

 let act: Activities = [.eating, .programming, .saveGotham]

 act.contains(.breathing). //true /* this is unexpected */
 act.contains(.saveGotham) //true

Хотя массив не содержит значения ".breathing", он все равно возвращает true. Я изменил одну и ту же структуру с помощью другого rawValue

 struct Activities: OptionSet {
      let rawValue: Int
      static let eating = Activities(rawValue: 1)
      static let programming = Activities(rawValue: 8)
      static let breathing = Activities(rawValue: 16)
      static let saveGotham = Activities(rawValue: 32) 
}

 let act: Activities = [.eating, .programming, .saveGotham]

 act.contains(.breathing). //false
 act.contains(.saveGotham) //true

и получил желаемый результат. было бы здорово, если бы кто-то пролил свет на проблему и объяснил, как работает "OptionSet".

Спасибо.

swift


2 ответа


1 Решение Martin R [2018-02-02 15:14:00]

Протокол OptionSet предназначен для

... представляют типы битов, где отдельные биты представляют собой элементы набора.

В твоем случае,

let act: Activities = [.eating, .programming, .saveGotham]
print(act.rawValue) // 7

хранится как целое число, содержащее BITWISE OR исходных значений (1 | 2 | 4 = 7) и

  act.contains(.breathing). //true /* this is unexpected */

если BITWISE AND 7 & 3 отличен от нуля (это так).

Поэтому вам не следует использовать последовательные исходные значения, но значения двух значений, т.е. Каждое из взаимоисключающих значений представлено одной битовой позицией:

struct Activities: OptionSet {
    let rawValue: Int
    static let eating = Activities(rawValue: 1)
    static let programming = Activities(rawValue: 2)
    static let breathing = Activities(rawValue: 4)
    static let saveGotham = Activities(rawValue: 8)
}

или эквивалентно:

struct Activities: OptionSet {
    let rawValue: Int
    static let eating = Activities(rawValue: 1 << 0)
    static let programming = Activities(rawValue: 1 << 1)
    static let breathing = Activities(rawValue: 1 << 2)
    static let saveGotham = Activities(rawValue: 1 << 3)
}

Теперь все работает так, как ожидалось:

let act: Activities = [.eating, .programming, .saveGotham]
print(act.rawValue) // 11
print(act.contains(.breathing)) // false
print(act.contains(.saveGotham)) // true

1 Sandeep [2018-02-02 15:17:00]

На странице документации для OptionSet,

Вы используете протокол OptionSet для представления типов битов, где отдельные биты представляют собой элементы набора. Принятие этого протокола в ваших пользовательских типах позволяет выполнять связанные с набором операции, такие как тесты членства, союзы и пересечения этих типов. Более того, при применении с использованием определенных критериев принятие этого протокола не требует дополнительной работы с вашей стороны.

При создании набора параметров добавьте свойство rawValue в объявление типа. Свойство rawValue должно иметь тип, соответствующий протоколу FixedWidthInteger, например Int или UInt8. Затем создайте уникальные параметры как статические свойства вашего настраиваемого типа, используя уникальные полномочия двух (1, 2, 4, 8, 16 и т.д.) Для каждого отдельного значения свойств свойств, чтобы каждое свойство могло быть представлено одним битом типы raw value.

Необработанный тип должен быть установленным битом. Итак, что происходит с вашим кодом выше,

let act: Activities = [.eating,.programming,.saveGotham]

Поскольку питание равно 1 эквиваленту 00000001, программирование равно 2, которое равно 00000010, saveGotham - 4, то есть 00000100. Таким образом, ваша переменная act теперь устанавливает все три бита в конце, которые могут быть представлены следующим образом 00000111.

00000111 теперь также включает дыхание.

В приведенной выше заметке вы должны сделать уникальные побитовые значения уникальными.

И вы можете взять пример из документов,

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]
}