用枚舉表示狀態(tài), 選項, 狀態(tài)碼

由于OC基于C語言,所以C語言有的功能它都有.枚舉(enum)就是其中之一.系統(tǒng)框架中頻繁使用枚舉,在用一系列常量來表示錯誤狀態(tài)碼或可組合的選項是,極宜用枚舉為其命名.由于C++11標準擴充了枚舉的特性,所以最新版系統(tǒng)框架使用了"強類型"的枚舉.OC也得益于C++11標準.

枚舉只是一種常量的命名方式,某個對象所經(jīng)歷的各種狀態(tài)就是可以定義為一個簡單的枚舉集合.

enum UIAlertActionStyle {
    UIAlertActionStyleDefault,
    UIAlertActionStyleCancel,
    UIAlertActionStyleDestructive
};

由于每種狀態(tài)都有一個便于理解的值來表示,所以這樣寫出來的代碼更易讀懂.編譯器會為枚舉分配一個獨有的編號,從0開始,每個枚舉遞增1.實現(xiàn)枚舉所用的數(shù)據(jù)類型取決于編譯器,不過其二進制位的個數(shù)必須能完全表示下枚舉編號才行.

然而定義枚舉變量的方式卻不太簡潔:

enum  UIAlertActionStyle  state =  UIAlertActionStyleDefault;

要是每次不用敲enum,只寫UIAlertActionStyle就爽了.于是可以這樣寫:

enum UIAlertActionStyle {
    UIAlertActionStyleDefault,
    UIAlertActionStyleCancel,
    UIAlertActionStyleDestructive
};
typedef enum UIAlertActionStyle  UIAlertActionStyle;

現(xiàn)在就可以這樣定義枚舉變量了:

 UIAlertActionStyle  state =  UIAlertActionStyleDefault;

C++11標準修訂了枚舉的某些特性,其中一項改動是:可以指明用何種"底層數(shù)據(jù)類型"來保存枚舉類型的變量.這樣做的好處是,可以向前聲明枚舉變量了.若不指定底層數(shù)據(jù)類型,則無法向前聲明枚舉類型.因為編譯器不知道底層數(shù)據(jù)類型的大小,所以在用到此枚舉類型時,也就不知道該給變量分配多少內(nèi)存空間.

指定底層數(shù)據(jù)類型所有的語法是:

enum CWGConnectionStateConnectionState: NSInteger {/* ... */};

這樣就保證了枚舉底層數(shù)據(jù)類型是NSinteger.也可以在向前聲明時指定底層數(shù)據(jù)類型:

enum  CWGConnectionStateConnectionState: NSInteger;

還可以不使用編譯器所分配的序號,而是手工指定某個枚舉成員所對應的值.

enum CWGConnectionStateConnectionState {
  CWGConnectionStateDisconnected = 1,
  CWGConnectionStateConnecting,
  CWGConnectionStateConnected,
};

這樣的話,上面中的每一個枚舉變量的值就是CWGConnectionStateConnecting值為2,CWGConnectionStateConnected值為3.

還有一種情況應該使用枚舉類型,那就是定義選項的時候.如果這些選項可以彼此組合,那么更應該如此.只要枚舉定義的對,那么各選項之間就可以通過"按位或操作符"來組合.例如在iOSUI框架下,有用來表示某個視圖應該如何在水平或垂直方向上調(diào)整大小:

enum UIViewAutoresizing {
    UIViewAutoresizingNone  = 0,
    UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
    UIViewAutoresizingFlexibleWidth = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin = 1 << 3,
    UIViewAutoresizingFlexibleHeight = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

每個選項都可以啟動或禁用.使用上訴方式來定義枚舉值既可以保證這一點.因為在每個枚舉值所對應的二進制表示中,只有1個二進制的值是1,用"按位或操作符"可組合對個選項.例如:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight,用按位與操作符既可以判斷出是否已經(jīng)啟動某個選項:

enum UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

if(resizing & UIViewAutoresizingFlexibleWidth) {
  // UIViewAutoresizingFlexibleWidth is set
};

系統(tǒng)庫中頻繁使用這個方法,iOS UI框架中的UIKit里還有一個例子, 用枚舉值告訴系統(tǒng)視圖所支持的設備顯示方向,開發(fā)者需要實現(xiàn)一個supportedInterfaceOrientations的方法.

- (NSUInteger)supportedInterfaceOrientations {
  return  UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;
}

Foundation框架中定義了一些輔助的宏,用這些宏定義枚舉時,可以指定用于保存枚舉值的底層數(shù)據(jù)類型.而且這些宏具備向后兼容能力,如果目標平臺的編譯器支持新標準,那么就使用新式語法,否則改用舊式語法.這些宏是用#define預處理指令來定義的.

  • 普通的枚舉類型:
typedef NS_ENUM (NSUInteger, CWGConnectionState) {
    CWGConnectionStateDisconnected,
    CWGConnectionStateConnecting,
    CWGConnectionStateConnected,
};
  • 帶有一系列枚舉選項類型:
typedef NS_OPTIONS (NSUInteger, CWGPermittedDirection) {
    CWGPermittedDirectionUp = 1 << 0;
    CWGPermittedDirectionLeft = 1 << 1;
    CWGPermittedDirectionDown = 1 << 2;
    CWGPermittedDirectionRight = 1 << 3;
};

用NS_ENUM宏所定義的枚舉類型展開之后就是:

typedef enum CWGConnectionState : NSUInteger CWGConnectionState;
enum CWGConnectionState : NSUInteger {
    CWGConnectionStateDisconnected,
    CWGConnectionStateConnecting,
    CWGConnectionStateConnected,
}

注意:根據(jù)是否要代碼按C++模式編譯,NS_OPTION宏的定義方式也有所不同,如果不按照C++編譯,那么其展開方式和NS_ENUM完全一樣.若按照C++編譯,則展開后的代碼略有不同.原因在于,用按位或運算來操作兩個枚舉值時,C++編譯模式的處理辦法和非C++模式不一樣.在用或運算符操作兩個枚舉值時,C++認為運算結果的數(shù)據(jù)類型應該是枚舉底層數(shù)據(jù)類型.也就是NSUInteger,而且C++不允許將這個底層類型"隱式轉(zhuǎn)換"為枚舉類型本身.所以,在C++模式下應該用另外一種方式定義NS_OPTION宏.以便省去類型轉(zhuǎn)換操作.鑒于此,凡是需要以按位或操作來組合枚舉都應使用NS_OPTION定義,若是枚舉不需要組合,則應該使用NS_ENUM來定義.

能使用到枚舉的情況還有很多,前面已經(jīng)提到了,枚舉可以表示選項和狀態(tài),然而還有許多東西也可以用枚舉來表示.比如狀態(tài)碼就是個很好的例子.可以吧邏輯含義相識的一組狀態(tài)碼放到一個枚舉集合里.而不要用#define預處理命令或常量來定義,以枚舉來表示樣式更適合.假如創(chuàng)建某個UI元素時可以使用不同的樣式,那么在這樣的情況下就最應該把樣式聲明為枚舉類型了.

最后再講一種枚舉的用法,就是在switch語句里.有時可以這樣定義:

typedef NS_ENUM (NSUInteger, CWGConnectionState) {
    CWGConnectionStateDisconnected,
    CWGConnectionStateConnecting,
    CWGConnectionStateConnected,
};

switch (_currentState) {
  CWGConnectionStateDisconnected:
  // Handle connecting state
      break;
  CWGConnectionStateConnecting:
 // Handle disconnected state
      break;
  CWGConnectionStateConnected:
 // Handle connected state
      break;
};

我們總習慣在switch語句中加上defaul分支,然后,若是用枚舉來定義狀態(tài)機,則最好不要有default分支,這樣的話, 如果稍后又加了一種狀態(tài),那么編譯器就會發(fā)出警告信息,提示新加入的狀態(tài)并未在switch分支中處理.假如寫上了default分支,那么它就會處理這個新狀態(tài),從而導致編譯器不發(fā)出警告信息.用NS_ENUM定義其他枚舉類型時也要注意此問題.例如:在定義代表UI元素樣式的枚舉時,通常要確保switch語句能正確處理所有樣式.

總結:

  • 應該用枚舉來表示狀態(tài)機的狀態(tài),傳遞給方法的選項以及狀態(tài)碼等值,給這些值起個易懂的名字.
  • 如果把傳遞給某個方法的選項表示為枚舉類型,而多個選項又可同時使用,那么就將各選項值定義為2的冪,以便通過按位或操作將其組合起來.
  • 用NS_ENUM與NS_OPTIONS宏來定義枚舉類型.并指明其底層數(shù)據(jù)類型.這樣做可以確保枚舉是用開發(fā)者所選的底層數(shù)據(jù)類型實現(xiàn)出來.而不會采用編譯器所選的類型.
  • 在處理枚舉類型的switch語句中不要實現(xiàn)default分支,這樣的話,加入枚舉之后,編譯器就會提示開發(fā)者:switch語句并未處理所有枚舉類型.
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

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