1、在以一系列常量來表示錯(cuò)誤狀態(tài)碼或可組合的選項(xiàng)時(shí)型诚,極宜使用枚舉為其命名狰贯。
2涵紊、枚舉只是一種常量命名方式幔摸。某個(gè)對象所經(jīng)歷的各種狀態(tài)就可以定義為一個(gè)簡單的枚舉集。
3驱负、定義枚舉變量的方式不太簡潔跃脊,要依如下語法編寫:
enum EOCConnectionState state = EOCConnectionStateDisconnection;
若是每次不用敲入 enum 而只需寫 EOCConnectionState 就好了酪术。要想這樣做,則需使用typedef關(guān)鍵字重新定義枚舉類型:
1. enum EOCConnectionState {
2. EOCConnectionStateDisconnected,
3. EOCConnectionStateConnecting,
4. EOCConnectionStateConnected,
5. };
6. typedef enum EOCConnectionState EOCConnectionState;
現(xiàn)在可以用簡寫的 EOCConnectionState 來代替完整的enum EOCConnectionState 了:
1.EOCConnectionState state = EOCConnectionStateDisconnected;
4橡疼、還有一種情況應(yīng)該使用枚舉欣除,那就是定義選項(xiàng)的時(shí)候挪略。
5瘟檩、
1. enum UIViewAutoresizing {
2. UIViewAutoresizingNone = 0,
3. UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
4. UIViewAutoresizingFlexibleWidth = 1 << 1,
5. UIViewAutoresizingFlexibleRightMargin = 1 << 2,
6. UIViewAutoresizingFlexibleTopMargin = 1 << 3,
7. UIViewAutoresizingFlexibleHeight = 1 << 4,
8. UIViewAutoresizingFlexibleBottomMargin = 1 << 5,
9. }
每個(gè)選項(xiàng)均可啟用或禁用,使用上述方式來定義枚舉值即可保證這一點(diǎn)卓研,因?yàn)樵诿總€(gè)枚舉值所對應(yīng)的二進(jìn)制表示中奏赘,只有1個(gè)二進(jìn)制位的值是1太惠。用“按位或操作符”可組合多個(gè)選項(xiàng),例如:UIViewAutoResizingFlexibleWidth| UIViewAutoresizingFlexibleHeight
梁只。圖1-2列出了每個(gè)枚舉成員的二進(jìn)制值搪锣,并演示了剛才那兩個(gè)枚舉組合之后的值彩掐。用“按位與操作符”(bitwise AND operator)即可判斷出是否已啟用某個(gè)選項(xiàng):
1. enum UIViewAutoresizing resizing =
2. UIViewAutoresizingFlexibleWidth |
3. UIViewAutoresizingFlexibleHeight;
4. if (resizing & UIViewAutoresizingFlexibleWidth) {
5. // UIViewAutoresizingFlexibleWidth is set
6. }
6、Foundation框架中定義了一些輔助的宏狗超,用這些宏來定義枚舉類型時(shí)努咐,也可以指定用于保存枚舉值的底層數(shù)據(jù)類型麦撵。這些宏具備向后兼容(backward compatibility)能力,如果目標(biāo)平臺的編譯器支持新標(biāo)準(zhǔn)免胃,那就使用新式語法羔沙,否則改用舊式語法扼雏。這些宏是用#define
預(yù)處理指令來定義的夯膀,其中一個(gè)用于定義像 EOCConnectionState
這種普通的枚舉類型,另一個(gè)用于定義像UIViewAutoresizing
這種包含一系列選項(xiàng)的枚舉類型蝴蜓,其用法如下:
1. typedef NS_ENUM(NSUInteger, EOCConnectionState) {
2. EOCConnectionStateDisconnected,
3. EOCConnectionStateConnecting,
4. EOCConnectionStateConnected,
5. };
6.
typedef NS_OPTIONS(NSUInteger, EOCPermittedDirection) {
7. EOCPermittedDirectionUp = 1 << 0,
8. EOCPermittedDirectionDown = 1 << 1,
9. EOCPermittedDirectionLeft = 1 << 2,
10. EOCPermittedDirectionRight = 1 << 3,
11. };
這些宏的定義如下:
1. #if (__cplusplus && __cplusplus >= 201103L&&
2. (__has_extension(cxx_strong_enums) ||
3. __has_feature(objc_fixed_enum))
4. ) ||
5. (!__cplusplus && __has_feature(objc_fixed_enum))
6. #define NS_ENUM(_type, _name)
7. enum _name : _type _name; enum _name : _type
8. #if (__cplusplus)
9. #define NS_OPTIONS(_type, _name)
10. type _name; enum : _type
11. #else
12. #define NS_OPTIONS(_type, _name)
13. enum _name : _type _name; enum _name : _type
14. #endif
15. #else
16. #define NS_ENUM(_type, _name) _type _name; enum
17. #define NS_OPTIONS(_type, _name) _type _name; enum
18. #endif
由于需要分別處理不同情況,所以上述代碼用多種方式來定義這兩個(gè)宏押袍。第一個(gè) #if 用于判斷編譯器是否支持新式枚舉。其中所用的布爾邏輯看上去相當(dāng)復(fù)雜汽馋,不過其意思就是想判斷編譯器是否支持新的枚舉特性惭蟋。如果不支持告组,那么就用老式語法來定義枚舉癌佩。
如果持新特性,那么用 NS_ENUM 宏所定義的枚舉類型展開之后就是:
1. typedef enum EOCConnectionState : NSUInteger EOCConnectionState;
2. enum EOCConnectionState : NSUInteger {
3. EOCConnectionStateDisconnected,
4. EOCConnectionStateConnecting,
5. EOCConnectionStateConnected,
6. };
7我碟、根據(jù)是否要將代碼按 C++ 模式編譯矫俺,NS_OPTIONS 宏的定義方式也有所不同。如果不按 C++ 編譯友雳,那么其展開方式就和 NS_ENUM 相同押赊。若按 C++ 編譯,則展開后的代碼略有不同流礁。原因在于罗丰,用按位或運(yùn)算來操作兩個(gè)枚舉值時(shí)萌抵,C++ 編譯模式的處理辦法與非 C++ 模式不一樣。而上面已經(jīng)提到了萎坷,作為選項(xiàng)的枚舉值經(jīng)常需要用按位或運(yùn)算來組合哆档。在用或運(yùn)算操作兩個(gè)枚舉值時(shí)住闯,C++ 認(rèn)為運(yùn)算結(jié)果的數(shù)據(jù)類型應(yīng)該是枚舉的底層數(shù)據(jù)類型,也就是NSUInteger插佛。而且 C++ 不允許將這個(gè)底層類型“隱式轉(zhuǎn)換”(implicit cast)為枚舉類型本身雇寇。我們用 EOCPermittedDirection 來演示一下蚌铜,假設(shè)按 NS_ENUM 方式將其展開:
1. typedef enum EOCPermittedDirection : int EOCPermittedDirection;
2. enum EOCPermittedDirection : int {
3. EOCPermittedDirectionUp = 1 << 0,
4. EOCPermittedDirectionDown = 1 << 1,
5. EOCPermittedDirectionLeft = 1 << 2,
6. EOCPermittedDirectionRight = 1 << 3,
7. };
然后考慮下列代碼:
1. EOCPermittedDirection permittedDirections =
2. EOCPermittedDirectionLeft | EOCPermittedDirectionUp;
若編譯器按 C++ 模式編譯(也可能是按Objective-C++模式編譯)囚痴,則會給出下列錯(cuò)誤信息:
1. error: cannot initialize a variable of type
2. 'EOCPermittedDirection' with an rvalue of type 'int
如果想編譯這行代碼深滚,就要將按位或操作的結(jié)果顯式轉(zhuǎn)換(explicit cast)為EOCPermittedDirection。所以痴荐,在 C++ 模式下應(yīng)該用另一種方式定義 NS_OPTIONS 宏蹬昌,以便省去類型轉(zhuǎn)換操作皂贩。鑒于此明刷,凡是需要以按位或操作來組合的枚舉都應(yīng)使用 NS_OPTIONS 定義。若是枚舉不需要互相組合辈末,則應(yīng)使用 NS_ENUM 來定義挤聘。
8组去、最后再講一種枚舉的用法步淹,就是在switch語句里。有時(shí)可以這樣定義:
1. typedef NS_ENUM(NSUInteger, EOCConnectionState) {
2. EOCConnectionStateDisconnected,
3. EOCConnectionStateConnecting,
4. EOCConnectionStateConnected,
5. };
6.
7. switch (_currentState) {
8. EOCConnectionStateDisconnected:
9. // Handle disconnected state
10. break;
11. EOCConnectionStateConnecting:
12. // Handle connecting state
13. break;
14. EOCConnectionStateConnected:
15. // Handle connected state
16. break;
17. }
我們總習(xí)慣在 switch 語句中加上 default 分支缭裆。然而,若是用枚舉來定義狀態(tài)機(jī)(state machine)辛燥,則最好不要有default分支挎塌。這樣的話勃蜘,如果稍后又加了一種狀態(tài)缭贡,那么編譯器就會發(fā)出警告信息,提示新加入的狀態(tài)并未在switch分支中處理阳惹。假如寫上了default分支快鱼,那么它就會處理這個(gè)新狀態(tài)抹竹,從而導(dǎo)致編譯器不發(fā)警告信息窃判。用 NS_ENUM 定義其他枚舉類型時(shí)也要注意此問題。例如喇闸,在定義代表 UI 元素樣式的枚舉時(shí)袄琳,通常要確保 switch 語句能正確處理所有樣式。
要點(diǎn)
① 應(yīng)該用枚舉來表示狀態(tài)機(jī)的狀態(tài)燃乍、傳遞給方法的選項(xiàng)以及狀態(tài)碼等值唆樊,給這些值起個(gè)易懂的名字。
② 如果把傳遞給某個(gè)方法的選項(xiàng)表示為枚舉類型刻蟹,而多個(gè)選項(xiàng)又可同時(shí)使用逗旁,那么就將各選項(xiàng)值定義為 2 的冪,以便通過按位或操作將其組合起來座咆。
③ 用 NS_ENUM 與 NS_OPTIONS 宏來定義枚舉類型痢艺,并指明其底層數(shù)據(jù)類型。這樣做可以確保枚舉是用開發(fā)者所選的底層數(shù)據(jù)類型實(shí)現(xiàn)出來的介陶,而不會采用編譯器所選的類型堤舒。
④ 在處理枚舉類型的 switch 語句中不要實(shí)現(xiàn) default 分支舌缤。這樣的話,加入新枚舉之后,編譯器就會提示開發(fā)者:switch 語句并未處理所有枚舉囚似。