在进一步阅读时,关于使用 NS_OPTIONS 标记的 C 风格选项,它说:
Swift 还会导入使用 NS_OPTIONS 宏标记的选项。虽然选项的行为类似于导入的枚举,但选项也可以支持一些按位操作,例如“&”,“|”和“~”。 在 Objective-C 中,您可以使用常量零(0)表示空选项集。在 Swift 中,使用 nil 表示没有任何选项。
考虑到 Swift 中没有 options 值类型,我们该如何创建一个 C 风格的 options 变量来使用?
与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)
}
Swift 3建议使用空数组字面量,而不是提供none
选项:
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中,协议扩展处理了大部分样板代码,它们现在被导入为符合OptionSetType
的结构体。(自Swift 2 beta 2起,RawOptionSetType
已经消失。)声明要简单得多:
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 导入的 Objective-C 选项(例如 UIViewAutoresizing
),我们可以看到这些选项被声明为符合协议 RawOptionSetType
的 struct
,而该协议又符合 _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
,您可以使用类似于enum
的语法。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")
}
Xcode 6.1 Beta 2对RawOptionSetType
协议进行了一些更改(请参见Airspeedvelocity博客文章和Apple发行说明)。根据Nate Cook的示例,以下是更新后的解决方案。您可以像这样定义自己的选项集:
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]
}
您可以在这里找到它。
NS_OPTIONS
风格的类型被导入为新的OptionSetType
类型的子类型。由于新的Protocol Extensions特性和标准库中实现OptionSetType
的方式,您可以声明自己的类型来扩展OptionsSetType
并获得与导入的NS_OPTIONS
风格类型相同的所有函数和方法。 SetAlgebraType
协议中获取所有方法,例如从数组文字语法创建、包含查询、与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!")
}
等等。这里并没有实际地翻转位。这些是针对不透明值的设置操作。您可以在此处找到要点。
为了帮助其他人,我在这里提供一个额外的例子,说明是否可以组合复合选项。答案是肯定的,如果你习惯了好老的位字段,它们的组合方式就像你预期的那样:
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)"
|
组合选项,并使用&
检查组合选项是否包含特定选项,那么Nate Cook的答案的替代方法可能是这样的: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)
正如Rickster已经提到的,您可以在Swift 2.0中使用OptionSetType。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
}
}
关于使用带有多个选项的选项集创建沙盒和书签
let options:NSURL.BookmarkCreationOptions = [.withSecurityScope,.securityScopeAllowOnlyReadAccess]
let temp = try link.bookmarkData(options: options, includingResourceValuesForKeys: nil, relativeTo: nil)
RawOptionsSetType
进行了广泛描述:http://nshipster.com/rawoptionsettype/ - Klaas