列舉估計是大家工作中經常打交道的一種型別,那麼在OC和Swift中我們經常如何用它的呢?綜合多方面的使用,我們大體可以將列舉分為兩種方式,一種是普通的列舉,還有一種不普通的列舉(我屮艸芔茻,你這不是廢話嗎~),誒,第二種應該說是列舉中的各種可能同時存在,如果換成程式碼的語言就是列舉中的各個值有可能進行位操作運算的。那麼在程式碼裡面分別如何表示呢?
OC中將這兩種分別用NS_ENUM
與NS_OPTIONS
來區分,而Swift中則用普通的enum以及OptionSetType
來搞定。那麼該如何使用以及在使用過程中有什麼需要注意的呢?且聽下回分解(分解你大爺啊!)
咳咳,首先我們先來看看NS_ENUM
以及NS_OPTIONS
是個什麼東東吧~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#if (__cplusplus && __cplusplus >= 201103L \ && (__has_extension(cxx_strong_enums) \ || __has_feature(objc_fixed_enum)) \ ) || \ (!__cplusplus && __has_feature(objc_fixed_enum)) #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type #if (__cplusplus) #define NS_OPTIONS(_type, _name) _type _name; enum : _type #else #define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type #endif #else #define NS_ENUM(_type, _name) _type _name; enum #define NS_OPTIONS(_type, _name) _type _name; enum #endif |
其實呢,他就是一個巨集定義。那麼我們來一個一個的分析每個巨集定義是如何生效的。假設我們寫下下面這段話:
1 2 3 4 5 6 |
typedef NS_ENUM(NSUInteger, MyNSEnum) { MyNSEnumValue1, MyNSEnumValue2, MyNSEnumValue3, MyNSEnumValue4 }; |
那麼通過上面的巨集定義應該就會變成下面這段話:
1 2 3 4 5 6 7 8 9 10 |
// 情況一:如果滿足上面一堆的條件 typedef enum MyNSEnum : NSUInteger; enum MyNSEnum : NSUInteger { //具體內容 }; // 情況二:如果不滿足上面一堆的條件 typedef NSUInteger MyNSEnum; enum { //具體內容 }; |
可以看出來NS_ENUM
是做到向下相容了,在一些低版本的,不相容一些特性的情況下則用情況二的方式來定義列舉,而在新版本的情況下,則採用情況一來定義。當然對於上面一些條件,有興趣的小夥伴可以自己查查資料瞭解一下。
估計還有一部分小夥伴習慣直接用enum之類的來定義列舉,但是還是建議大家儘量用NS_ENUM
以及NS_OPTIONS
,畢竟蘋果提供了這個新的特性,而且它也支援了向下相容。並且現在大家的都習慣性用這個,如果寫這個也方面大家讀懂以及直觀看懂是NS_ENUM
還是NS_OPTIONS
。而且這也是目前許多網站和資料推薦的做法,也是一個共識。(路人甲:我就不用,我就不用,你特麼來打我啊~)額,你老大,你了不起咯。
那麼除了這個好處以外還有沒有什麼其他的好處呢?在switch塊中,我們如果用到的是NS_ENUM
的話,儘量不要用default:
,這樣的話如果沒有寫全所有的列舉型別的話,會出現warning,這樣也方便了我們如果在使用列舉+switch
的過程中,如果新增了一個新的列舉的值,那麼同時也會出現warning。就像下圖:
然後普通的列舉就儘量用NS_ENUM
。對於那些可以同時存在的,比如一些配置項,一些控制某些事件的開關之類的列舉我們就儘量用NS_OPTIONS
,例如我們在用NSCalendarUnit
,還有動畫裡面的UIViewAnimationOptions
以及Autoresizing
裡面用到的UIViewAutoresizing
都是NS_OPTIONS
型別,而這種型別我們經常都會用位操作符來進行控制。
我們從UIKit中看下UIViewAutoresizing
吧,程式碼如下:
1 2 3 |
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) { UIViewAutoresizingNone = 0, UIViewAutoresizingFlexibleLeftMargin = 1 |
而通常NS_OPTIONS
都是這樣定義的,即通過所需的型別在二進位制中的某一個位置的不為0來進行操作,這樣也方便了我們通過或(或者與)來進行控制那些屬性是需要的,那些屬性不需要的來進行不同的操作。
看完OC中的做法,那麼我們來看看Swift中是怎麼操作的呢?估計普通的列舉大家都用到爛了,就是enum了,那我就不多說廢話了,對於NS_OPTIONS
在Swift中的替代方案是什麼呢?
如果用過Swift1.2的小夥伴估計想到了以前用的Xcode snippet,通常都是這個:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
struct : RawOptionSetType, BooleanType { let rawValue: UInt init(nilLiteral: ()) { self.value = 0 } init(_ value: UInt = 0) { self.value = value } init(rawValue value: UInt) { self.value = value } var boolValue: Bool { return value != 0 } var rawValue: UInt { return value } static var allZeros: { return self(0) } static var None: { return self(0b0000) } static var : { return self(0b0001) } // ... } |
看到這裡,估計小夥伴可以體會到在1.2的時候那酸爽真是夠了,如果自己手寫一行一行的敲,估計好浪費時間啊。所以之前都是通過用Xcode snippet來進行操作的。而在2.2中我們有事如何寫的呢?我們來嘗試一下:
1 2 3 4 5 6 7 8 |
struct NSCOptions : OptionSetType { let rawValue: UInt static let None = NSCOptions(rawValue: 0) static let Value1 = NSCOptions(rawValue: 0b0001) static let Value2 = NSCOptions(rawValue: 0b0010) static let Value3 = NSCOptions(rawValue: 0b0100) static let Value4 = NSCOptions(rawValue: 0b1000) } |
發現沒相對Swift1.2簡潔了不是一點兩點,是不是很高興。好噠,妹子上來親我一口吧~(pia~流氓)。咳咳,言歸正傳,是不是比之前方便很多,那麼我們來看下OptionSetType
是什麼鬼?
可以看得出來左邊這個就是我們經常enum中用來建立的構造方法,右邊這個從最上面說起,上面的Equatable
是用來判等用的,也就是我們經常用的!=
和==
的協議,而右邊這個則是我們之前說過的字面量裡面的陣列字面量(關於字面量一節小夥伴們可以看看字面量(Literal)),也就是我們可以用[]
來表示一組Options的集合,而不是通過|
來表達,這樣用起來也更加人性化。而SetAlgebraType
則提供了集合的操作,比如並集,交集,異或,插入等等。這裡就不對他們進行深究了,放過他們吧。俗話說得好,冤冤相報何時了~
好了,最終關於列舉我們就說到這裡吧,剩下的小夥伴們可以自己去玩玩。不過別玩出火喲~
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式