Swift에서 NS_OPTIONS 스타일 비트 마스크 열거를 만드는 방법은 무엇입니까?
C API와의 상호 작용에 대한 Apple의 설명서에서 C NS_ENUM
스타일 열거를 Swift 열거로 가져 오는 방법을 설명합니다 . 이것은 의미가 있으며 Swift의 열거 형은 enum
값 유형 으로 쉽게 제공되므로 자체 작성 방법을 쉽게 볼 수 있습니다.
아래에 NS_OPTIONS
표시된 C 스타일 옵션 에 대해 다음과 같이 말합니다 .
Swift는
NS_OPTIONS
매크로 로 표시된 옵션도 가져옵니다 . 옵션은 수입 열거에 유사하게 동작하는 반면, 옵션은 다음과 같은 몇 가지 비트 연산을 지원할 수&
,|
및~
. Objective-C에서 상수 0 (0
)으로 설정된 빈 옵션을 나타냅니다 . Swift에서는nil
옵션이 없음을 나타내는 데 사용 하십시오.
options
Swift에 값 유형 이없는 경우 C 스타일 옵션 변수를 작성하여 어떻게 작업 할 수 있습니까?
스위프트 3.0
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)
}
none
옵션 을 제공하는 대신 Swift 3 권장 사항은 단순히 빈 배열 리터럴을 사용하는 것입니다.
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")
}
스위프트 2.0
Swift 2.0에서 프로토콜 확장은이를위한 보일러 플레이트의 대부분을 관리하며, 이제는이를 준수하는 구조체로 가져옵니다 OptionSetType
. ( RawOptionSetType
Swift 2 베타 2부터 사라졌습니다.) 선언이 훨씬 간단합니다.
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")
}
스위프트 1.2
(스위프트에 의해 수입 된 목표 - C 옵션을 보면 UIViewAutoresizing
예를 들어,), 우리는 옵션이로 선언 된 것을 볼 수 있습니다 struct
해당 프로토콜을 준수 RawOptionSetType
차례에 부합 함을 선언에서에 _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
Apple 문서에 설명 된 것처럼 이 새로운 옵션 세트를 처리 할 수 있습니다. 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")
}
찾기 / 바꾸기없이 Swift 옵션 세트를 생성 하는 생성기를 만들었습니다 .
최신 : Swift 1.1 베타 3 수정.
Xcode 6.1 베타 2는 RawOptionSetType
프로토콜 을 일부 변경했습니다 (이 Airspeedvelocity 블로그 항목 및 Apple 릴리스 정보 참조 ).
Nate Cooks 예제를 기반으로 업데이트 된 솔루션이 있습니다. 다음과 같이 자신의 옵션 세트를 정의 할 수 있습니다.
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]
}
Swift 2 (현재 Xcode 7 베타의 일부로 베타)에서 NS_OPTIONS
스타일 유형을 새 OptionSetType
유형의 하위 유형으로 가져옵니다 . 새로운 프로토콜 확장 기능과 OptionSetType
표준 라이브러리에서 구현 된 방식 덕분에 OptionsSetType
가져온 NS_OPTIONS
스타일 유형 과 동일한 기능 및 메소드를 확장 하고 가져 오는 고유 한 유형을 선언 할 수 있습니다 .
그러나 이러한 함수는 더 이상 비트 산술 연산자를 기반으로하지 않습니다. C에서 비 독점 부울 옵션 세트를 사용하려면 필드에서 마스킹 및 비트를 처리해야합니다. 실제로, 옵션 세트 는 고유 한 아이템의 집합입니다. 따라서 배열 리터럴 구문에서 생성,와 같은 쿼리 ,로 마스킹 등 OptionsSetType
의 SetAlgebraType
프로토콜 에서 모든 메소드를 가져 옵니다 (어떤 멤버십 테스트에 어떤 재미있는 캐릭터를 사용해야하는지 기억할 필요가 없습니다!)contains
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에서 비트 마스크 의 표면 의미 를 원한다면, 일반적인 Swift 열거 형으로이를 수행 할 수있는 BitwiseOptions라는 간단한 "라이브러리"를 작성했습니다.
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!")
}
등등. 실제 비트는 여기서 뒤집 히지 않습니다. 이들은 불투명 한 값에 대해 설정된 작업입니다. 여기서 요점을 찾을 수 있습니다 .
우리가 필요한 유일한 기능이 옵션을 결합 |
하고 결합 된 옵션에 특정 옵션이 포함되어 있는지 확인 하는 방법 이라면 &
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)
복합 옵션을 결합 할 수 있는지 궁금해하는 다른 사람을 위해 추가 예를 게시하십시오. 당신은 할 수 있고, 당신이 좋은 오래된 비트 필드에 익숙하다면 기대하는 것처럼 결합합니다 :
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)"
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
}
}
다른 사람은 그것을 언급하지 않았으며 약간의 땜질 후에 난처하게 들었습니다. 그러나 스위프트 세트는 상당히 잘 작동하는 것 같습니다.
비트 마스크가 실제로 무엇을 나타내는 지에 대해 (벤 다이어그램으로 생각할 수 있습니까?) 생각하면 비어있는 세트 일 수 있습니다.
물론, 첫 번째 원칙에서 문제에 접근 할 때 비트 연산자의 편의성을 잃지 만 가독성을 향상시키는 강력한 세트 기반 방법을 얻습니다.
예를 들어 내 땜질은 다음과 같습니다.
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])
I find this nice because I feel it comes from a first principles approach to the problem -- much like Swift -- rather than trying to adapt C-style solutions.
Would also like to hear some Obj-C use cases that would challenge this different paradigm, where the integer raw values still shows merit.
In order to avoid hard coding the bit positions, which is unavoidable when using (1 << 0)
, (1 << 1)
, (1 << 15)
etc. or even worse 1
, 2
, 16384
etc. or some hexadecimal variation, one could first defines the bits in an enum
, then let said enum do the bit ordinal calculation:
// 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)
}
I use the following I need the both values I can get, rawValue for indexing arrays and value for 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
And if one needs more just add a computed property.
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"
}
}
}
re: Sandbox and bookmark creations using option sets with several options
let options:NSURL.BookmarkCreationOptions = [.withSecurityScope,.securityScopeAllowOnlyReadAccess]
let temp = try link.bookmarkData(options: options, includingResourceValuesForKeys: nil, relativeTo: nil)
solution to needing to combine options for creations, useful when not all options are mutually exclusive.
Nate's answer is good but I would make it DIY, like so:
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)
}
Use an Option Set Type, in swift 3 use 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]
}
'programing tip' 카테고리의 다른 글
표에서 1을 선택한다는 것은 무슨 의미입니까? (0) | 2020.07.04 |
---|---|
런타임시 기본 app.config 변경 (0) | 2020.07.04 |
NoMethodError : 레이크 11로 업그레이드 한 후 정의되지 않은 메소드 'last_comment' (0) | 2020.07.04 |
ASP.NET 세션 쿠키에서 보안 플래그를 설정하는 방법 (0) | 2020.07.04 |
자바 스크립트에서 변수 유형 찾기 (0) | 2020.07.04 |