1. 程式人生 > >Objective-C列舉型別詳解

Objective-C列舉型別詳解

最近看了一本OC進階的書,很多人推薦的Effective Objictive-C2.0,看到書中列舉型別的介紹,覺得很不錯,這裡為大家總結一下。

enum

由於Objective-C基於C語言,所以C語言有的功能它都有,其中之一就是列舉型別enum。列舉型別只是一種常亮命名方式,某個物件所經歷的各種狀態或者型別就可以定義為一個簡單的列舉型別,比如一個通訊錄或者聊天頁的頁面有不同的會話型別,根據型別的不同所展示的頁面或者操作對應也不相同。可以簡單的如下定義列舉型別:

enum ViewType {
    Contacts_Stranger,//陌生人
    Contacts_NormalFriend,//普通好友
Contacts_SpecialFriend,//特別關心好友 };

由於每種狀態都用一個便於理解的值來表示,所以這樣寫更為直觀,程式碼更易讀懂。編譯器會為列舉分配一個獨有的編號,預設從0開始,每個列舉遞增1。
然而定義列舉型別的方式卻不太簡潔,要依如下語法編寫:

    enum ViewType type = Stranger;

typedef enum

使用typedef來定義列舉型別則簡潔很多:

enum ViewType {
    Contacts_Stranger,//陌生人
    Contacts_NormalFriend,//普通好友
    Contacts_SpecialFriend,//特別關心好友
}; typedef enum ViewType ViewType;

或者這樣定義:

typedef enum  {
    Contacts_Stranger,//陌生人
    Contacts_NormalFriend,//普通好友
    Contacts_SpecialFriend,//特別關心好友
}ViewType;

就可以用簡寫的ViewType代替 enum ViewType了。
還可以不使用編譯器所分配的序號,而是手工指定某個列舉成員所對應的值,語法如下:

enum ViewType {
    Contacts_Stranger = 1,//陌生人
    Contacts_NormalFriend,//普通好友
Contacts_SpecialFriend,//特別關心好友 };

上述程式碼把Contacts_Stranger的值設為1,那麼接下來的幾個列舉值會在上一個的基礎上遞增1,則Contacts_SpecialFriend的值為3。

位移操作列舉定義

還有一種情況應該使用列舉型別,那就是定義選項的時候,若這些選項可以彼此組合,則更應如此,個選項之間可以通過“按位或操作符”來組合。例如,iOS的UI框架中有如下列舉型別,用來表示某個檢視應該如何在水平或者垂直方向上調整大小:

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

注:‘<<’為左移操作符,‘1 << 2’表示1左移2位,即為二進位制100。
每個選項均可啟用或者禁用,使用上述列舉定義即可保證這一點,因為上述每個列舉值中只有一個二進位制位為1。用“按位或操作符”可組合多個選項,比如:

    enum UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    if (resizing & UIViewAutoresizingFlexibleWidth) {
        //UIViewAutoresizingFlexibleWidth is set
    }

這裡寫圖片描述
系統庫中頻繁使用這個方法。iOS UI框架中的UIKit裡還有個例子,用列舉告訴系統檢視所支援的裝置顯示方向。這個列舉型別叫做UIInterfaceOrientationMask,開發者需要實現一個名為supportedInterfaceOrientations 方法,將檢視所支援的顯示方向告訴系統:

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

此外,一些小於等於三個的列舉值,可以不用位移操作列舉定義一樣也可以這樣使用,因為三個列舉值正好是0,1,2。例如系統UITableView中的tableView編輯型別的列舉型別定義:

typedef NS_ENUM(NSInteger, UITableViewCellEditingStyle) {
    UITableViewCellEditingStyleNone,
    UITableViewCellEditingStyleDelete,
    UITableViewCellEditingStyleInsert
};

在tableView的代理方法中依然可以“按位或操作符”來高速系統編輯樣式:

- ( UITableViewCellEditingStyle )tableView:( UITableView *)tableView editingStyleForRowAtIndexPath:( NSIndexPath *)indexPath
{
    return UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert;
}

但如果我們自己定義,還是規範的用位移操作列舉定義比較好。

NS_ENUM、NS_OPTIONS

你可能發現上面的列舉型別的定義和我們之前講的有些不一樣,出現了NS_ENUM這個關鍵字,並且格式也有些不同。這是因為Foundation框架中定義了一些輔助的巨集(巨集的介紹請看這裡:http://blog.csdn.net/liu1347508335/article/details/50824946)。用這些巨集來定義列舉型別時,可以指定用於儲存列舉值的底層資料型別。這些巨集是是用#define預處理指令來定義的,其中一個用於定義像ViewType這種普通的列舉型別,另一個用於定義像UIViewAutoresizing這種包含一些列選項的列舉型別,其用法如下:

typedef NS_ENUM(NSUInteger, ViewType) {
    Contacts_Stranger,//陌生人
    Contacts_NormalFriend,//普通好友
    Contacts_SpecialFriend,//特別關心好友
};
typedef NS_OPTIONS(NSUInteger, PermittedDirection) {
    PermittedDirectionUp    = 1 << 0,
    PermittedDirectionDown  = 1 << 1,
    PermittedDirectionLeft  = 1 << 2,
    PermittedDirectionRight = 1 << 3,
};

這兩個巨集的具體實現就不多介紹了,感興趣的可以去看一看。用NS_ENUM與NS_OPTIONS巨集來定義列舉型別,並指明其底層資料型別。這樣做可以確保列舉是用開發者所選的底層資料型別實現出來的,而不會採用編譯器所選的型別。
還有一點需要注意,當switch語句中應用列舉型別時,最好去掉default分支。這樣的話,如果稍後又加了一種狀態,那麼編譯器就會發出警告資訊,提示新加入的狀態並未在switch分支中處理。。假如寫上了default分支則不會發出警告。通常要確保switch語句能正確處理所有列舉樣式。

總結

應該用列舉來表示狀態機的狀態、傳遞給方法的選項以及狀態碼等值,給這些值起個易懂的名字。
如果把傳遞給某個方法的選項表示為列舉型別,而多個選項又可同時使用,那麼就將各選項值定義為2的冪,以便通過按位或操作將其組合起來。
用NS_ENUM與NS_OPTIONS巨集來定義列舉型別,並指明其底層資料型別。
在處理列舉型別的switch語句中不要實現default分支。