筆記-第5條 用枚舉表示狀態(tài)缸棵、選項(xiàng)谭期、狀態(tài)碼(規(guī)范)

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 語句并未處理所有枚舉囚似。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贯钩,一起剝皮案震驚了整個(gè)濱河市祸穷,隨后出現(xiàn)的幾起案子檩咱,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件以政,死亡現(xiàn)場離奇詭異废菱,居然都是意外死亡旁理,警方通過查閱死者的電腦和手機(jī)淹接,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斑鸦,“玉大人,你說我怎么就攤上這事嘱巾∥示校” “怎么了下愈?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵辖佣,是天一觀的道長。 經(jīng)常有香客問我端逼,道長寸爆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任析二,我火速辦了婚禮准谚,結(jié)果婚禮上愉棱,老公的妹妹穿的比我還像新娘。我一直安慰自己梅猿,他們只是感情好体斩,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布粱栖。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪忙迁。 梳的紋絲不亂的頭發(fā)上佛南,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天猜惋,我揣著相機(jī)與錄音,去河邊找鬼。 笑死峭状,一個(gè)胖子當(dāng)著我的面吹牛着帽,可吹牛的內(nèi)容都是我干的匪燕。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼割按,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了扒寄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤于樟,失蹤者是張志新(化名)和其女友劉穎路捧,沒想到半個(gè)月后杰扫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡相恃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冈欢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片餐弱。...
    茶點(diǎn)故事閱讀 38,599評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖囱晴,靈堂內(nèi)的尸體忽然破棺而出岸裙,到底是詐尸還是另有隱情,我是刑警寧澤速缆,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站恩闻,受9級特大地震影響艺糜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一破停、第九天 我趴在偏房一處隱蔽的房頂上張望翅楼。 院中可真熱鬧,春花似錦真慢、人聲如沸毅臊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽管嬉。三九已至,卻和暖如春朗鸠,著一層夾襖步出監(jiān)牢的瞬間蚯撩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工烛占, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胎挎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓忆家,卻偏偏與公主長得像犹菇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子芽卿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容