Dalam dokumentasi Apple tentang berinteraksi dengan C API, mereka mendeskripsikan cara NS_ENUM
pencacahan gaya-C yang diimpor sebagai pencacahan Swift. Ini masuk akal, dan karena enumerasi di Swift sudah tersedia sebagai enum
tipe nilai, mudah untuk melihat cara membuatnya sendiri.
Lebih jauh ke bawah, dikatakan ini tentang NS_OPTIONS
opsi C-style bertanda:
Swift juga mengimpor opsi yang ditandai dengan
NS_OPTIONS
makro. Sedangkan opsi berperilaku sama mantri diimpor, pilihan juga dapat mendukung beberapa operasi bitwise, seperti&
,|
, dan~
. Di Objective-C, Anda mewakili set opsi kosong dengan konstanta nol (0
). Di Swift, gunakannil
untuk menunjukkan tidak adanya opsi apa pun.
Mengingat bahwa tidak ada options
tipe nilai di Swift, bagaimana kita bisa membuat variabel opsi C-Style untuk digunakan?
sumber
RawOptionsSetType
: nshipster.com/rawoptionsettypeJawaban:
Swift 3.0.0
Hampir identik dengan Swift 2.0. OptionSetType diubah namanya menjadi OptionSet dan enum ditulis dengan huruf kecil berdasarkan konvensi.
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) }
Alih-alih memberikan
none
opsi, rekomendasi Swift 3 cukup menggunakan literal array kosong:let noOptions: MyOptions = []
Penggunaan lainnya:
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
Di Swift 2.0, ekstensi protokol menangani sebagian besar boilerplate untuk ini, yang sekarang diimpor sebagai struct yang sesuai
OptionSetType
. (RawOptionSetType
telah menghilang sejak Swift 2 beta 2.) Deklarasi ini jauh lebih sederhana: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) }
Sekarang kita dapat menggunakan semantik berbasis set dengan
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.0
Melihat pilihan Objective-C yang diimpor oleh Swift (
UIViewAutoresizing
misalnya), kita dapat melihat bahwa pilihan dinyatakan sebagaistruct
yang paling sesuai untuk protokolRawOptionSetType
, yang pada gilirannya sesuai dengan_RawOptionSetType
,Equatable
,RawRepresentable
,BitwiseOperationsType
, danNilLiteralConvertible
. Kita bisa membuatnya sendiri seperti ini: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) } }
Sekarang kita dapat menangani rangkaian opsi baru ini
MyOptions
, seperti yang dijelaskan dalam dokumentasi Apple: Anda dapat menggunakanenum
sintaks -seperti:let opt1 = MyOptions.FirstOption let opt2: MyOptions = .SecondOption let opt3 = MyOptions(4)
Dan itu juga berperilaku seperti kita mengharapkan opsi untuk berperilaku:
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") }
Saya telah membangun generator untuk membuat satu set opsi Swift tanpa semua menemukan / mengganti.
Terbaru: Modifikasi untuk Swift 1.1 beta 3.
sumber
value
fileUInt32
. Anda juga tidak perlu menentukan salah satu fungsi, fungsi yang relevan sudah ditentukan untukRawOptionSet
s (misalnyafunc |<T : RawOptionSet>(a: T, b: T) -> T
)UInt
? Ini bekerja dengan baik untuk saya.enum CollisionTypes: UInt32 { case Player = 1 case Wall = 2 case Star = 4 case Vortex = 8 case Finish = 16 }
Xcode 6.1 Beta 2 membawa beberapa perubahan pada
RawOptionSetType
protokol (lihat entri blog Airspeedvelocity ini dan catatan rilis Apple ).Berdasarkan contoh Nate Cooks, berikut adalah solusi yang diperbarui. Anda dapat menentukan set opsi Anda sendiri seperti ini:
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) } }
Kemudian dapat digunakan seperti ini untuk mendefinisikan variabel:
let opt1 = MyOptions.FirstOption let opt2:MyOptions = .SecondOption let opt3 = MyOptions(4)
Dan seperti ini untuk menguji bit:
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") }
sumber
Contoh Swift 2.0 dari dokumentasi:
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] }
Anda bisa menemukannya di sini
sumber
Di Swift 2 (saat ini beta sebagai bagian dari Xcode 7 beta),
NS_OPTIONS
tipe-gaya diimpor sebagai subtipe dariOptionSetType
tipe baru . Dan berkat fitur Ekstensi Protokol baru dan caranyaOptionSetType
diimplementasikan di pustaka standar, Anda dapat mendeklarasikan tipe Anda sendiri yang memperluasOptionsSetType
dan mendapatkan semua fungsi dan metode yang sama dengan yangNS_OPTIONS
didapat tipe gaya- impor .Tetapi fungsi tersebut tidak lagi didasarkan pada operator aritmatika bitwise. Bekerja dengan sekumpulan opsi Boolean non-eksklusif di C membutuhkan bit masking dan twiddling di bidang adalah detail implementasi. Sungguh, satu set opsi adalah satu set ... kumpulan barang-barang unik. Jadi
OptionsSetType
dapatkan semua metode dariSetAlgebraType
protokol, seperti pembuatan dari sintaks literal array, kueri seperticontains
, masking denganintersection
, dll. (Tidak perlu lagi mengingat karakter lucu mana yang akan digunakan untuk tes keanggotaan mana!)sumber
//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` }
sumber
Jika Anda tidak perlu bekerja sama dengan Objective-C dan hanya ingin semantik permukaan bit mask di Swift, saya telah menulis "library" sederhana yang disebut BitwiseOptions yang dapat melakukan ini dengan enumerasi Swift biasa, misalnya:
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!") }
dan seterusnya. Tidak ada bit aktual yang dibalik di sini. Ini adalah operasi set pada nilai buram. Anda dapat menemukan intinya di sini .
sumber
Seperti yang telah disebutkan Rickster, Anda dapat menggunakan OptionSetType di Swift 2.0. Jenis NS_OPTIONS diimpor sesuai dengan
OptionSetType
protokol, yang menampilkan antarmuka set-like untuk opsi:struct CoffeeManipulators : OptionSetType { let rawValue: Int static let Milk = CoffeeManipulators(rawValue: 1) static let Sugar = CoffeeManipulators(rawValue: 2) static let MilkAndSugar = [Milk, Sugar] }
Ini memberi Anda cara kerja ini:
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 } }
sumber
Jika satu-satunya fungsi yang kami perlukan adalah cara untuk menggabungkan opsi dengan
|
dan memeriksa apakah opsi gabungan berisi opsi tertentu dengan&
alternatif jawaban Nate Cook bisa jadi ini:Buat opsi
protocol
dan beban berlebih|
dan&
: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 } }
Sekarang kita dapat membuat pilihan struct lebih sederhana seperti ini:
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) } }
Mereka dapat digunakan sebagai berikut:
func myMethod(#options: MyOptions) { if options & .One { // Do something } } myMethod(options: .One | .Three)
sumber
Hanya memposting contoh tambahan untuk siapa pun yang bertanya-tanya apakah Anda dapat menggabungkan opsi gabungan. Anda bisa, dan mereka bergabung seperti yang Anda harapkan jika Anda terbiasa dengan bitfield lama yang bagus:
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
Ini meratakan set
[.AB, .X]
menjadi[.A, .B, .X]
(setidaknya secara semantik):print(state) // 0b111 as expected: "State(rawValue: 7)" print(State.AB) // 0b11 as expected: "State(rawValue: 3)"
sumber
Tidak ada orang lain yang menyebutkannya - dan saya agak melakukan kesalahan padanya setelah beberapa mengutak-atik - tetapi Swift Set tampaknya bekerja dengan cukup baik.
Jika kita berpikir (mungkin untuk diagram Venn?) Tentang apa yang sebenarnya diwakili oleh bit mask, itu adalah set yang mungkin kosong.
Tentu saja, dalam mendekati masalah dari prinsip pertama, kami kehilangan kenyamanan operator bitwise, tetapi mendapatkan metode berbasis set yang kuat yang meningkatkan keterbacaan.
Ini contoh mengutak-atik saya:
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])
Saya merasa ini bagus karena saya merasa ini berasal dari pendekatan prinsip pertama untuk masalah - seperti Swift - daripada mencoba menyesuaikan solusi C-style.
Juga ingin mendengar beberapa kasus penggunaan Obj-C yang akan menantang paradigma yang berbeda ini, di mana nilai mentah integer masih menunjukkan manfaat.
sumber
Untuk menghindari hard coding posisi bit, yang tidak dapat dihindari ketika menggunakan
(1 << 0)
,(1 << 1)
,(1 << 15)
dll atau bahkan lebih buruk1
,2
,16384
dll atau beberapa variasi heksadesimal, yang pertama bisa mendefinisikan bit dalamenum
, kemudian biarkan kata enum melakukan sedikit ordinal perhitungan:// 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) }
sumber
Saya menggunakan yang berikut ini, saya membutuhkan kedua nilai yang bisa saya peroleh, rawValue untuk mengindeks array dan nilai untuk bendera.
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
Dan jika seseorang membutuhkan lebih banyak, tambahkan saja properti yang dihitung.
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" } } }
sumber
re: Kreasi sandbox dan bookmark menggunakan set opsi dengan beberapa opsi
let options:NSURL.BookmarkCreationOptions = [.withSecurityScope,.securityScopeAllowOnlyReadAccess] let temp = try link.bookmarkData(options: options, includingResourceValuesForKeys: nil, relativeTo: nil)
solusi untuk kebutuhan menggabungkan opsi untuk kreasi, berguna ketika tidak semua opsi saling eksklusif.
sumber
Jawaban Nate bagus tapi saya akan membuatnya DIY, seperti ini:
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) }
sumber
Gunakan Jenis Set Opsi, dalam penggunaan 3 cepat
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] }
sumber