iOS(三)OC中的枚舉(NS_ENUM和NS_OPTION)

對于OC中的枚舉類型,雖然知道有NS_ENUM和NS_OPTION,然而并不是十分清楚它們之間的區別。另外,也很好奇,OC中為什么幾乎不使用enum,以及這三種枚舉類型之間的差異。

通過查閱資料,同時編寫代碼調試,總算弄清楚了之前的問題,所以寫這篇文章來進行總結。

enum的局限性

在枚舉的聲明中,使用typedef可以使得枚舉變量的聲明更簡單。

typedef enum {
    FlyStateOne,
    FlyStateTwo,
    FlyStateThree
}FlyState;

enum FlyTypeState{
    FlyTypeOne,
    FlyTypeTwo,
    FlyTypeThree
};
FlyState  state;
typedef enum  FlyTypeState state;

C++11標準擴充了枚舉的類型,通過新式枚舉,可以指明枚舉的底層數據類型。這樣,就可以確定給枚舉變量分配多少空間,從而能夠向前聲明枚舉變量。

而對于enum來說,不能直接在使用typedef的同時,確定枚舉的底層數據類型。

typedef enum {
    FlyStateOne,
    FlyStateTwo,
    FlyStateThree
}FlyState;

enum FlyState:NSInteger{ //設置底層數據類型為NSInteger
    FlyStateOne,
    FlyStateTwo,
    FlyStateThree
};

上面的兩種聲明方式,都是正確的,而下面的這種聲明方式是錯的:

typedef enum FlyState1:NSInteger{
    FlyStateOne1,
    FlyStateTwo1,
    FlyStateThree1
};

enum如果想在使用typedef的同時,確定底層數據類型,就只能使用兩條語句:

typedef enum FlyState:NSInteger FlyState;
enum FlyState : NSInteger {
    FlyStateOne,
    FlyStateTwo,
    FlyStateThree
};

這樣,很顯然在寫代碼的時候比較麻煩。

NS_ENUM和enum的不同

NS_ENUM是一個OC中的宏,可以判斷編譯器能否采用新式枚舉:如果不能,那么效果等同于僅僅使用typedef的enum;如果能夠采用新式枚舉,那么NS_ENUM所定義的枚舉類型,就是處理后的enum類型,可以在使用typedef的同時,指定底層數據類型。
比如:

typedef NS_ENUM(NSInteger, FlyState) {
    FlyStateOne,
    FlyStateTwo,
    FlyStateThree
};

如果支持新式枚舉,展開之后,就是:

typedef enum FlyState:NSInteger FlyState;
enum FlyState : NSInteger {
    FlyStateOne,
    FlyStateTwo,
    FlyStateThree
};

此時,既指定了數據類型,又可以使用:

FlyState state = FlyStateOne; 

很明顯,NS_ENUM就是對enum的一種封裝。所以,在OC中,幾乎不使用enum,都是使用NS_ENUM和NS_OPTION。

按位或 枚舉的作用

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

需要同時滿足多個枚舉條件時,使用按位或操作來進行組合。 比如:

UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleTopMargin

用于表示同時限制右邊距和頭部邊距

NS_ENUM和NS_OPTIONS的不同

NS_OPTIONS也是OC中的一個宏。

在用或運算處理兩個枚舉類型時,C++ 認為枚舉結果的數據類型應該是枚舉的底層數據類型(即NSUInteger),而且C++不允許將這個底層類型隱式轉換成枚舉類型本身。
比如:

typedef NS_ENUM(NSUInteger, UIViewAutoresizing) {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

此時:

UIViewAutoresizing viewModel =UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight

得到的枚舉結果的類型是NSUInteger,而不是UIViewAutoresizing。編譯器會報錯。

針對這種情況,NS_OPTIONS做了處理,區分了C++ 模式編譯和非C++ 模式編譯。在非C++ 編譯模式下,NS_OPTIONS和NS_ENUM是一樣的。對于C++模式編譯,NS_OPTIONS會做一些特殊處理,保證枚舉結果的類型正確。

總結

NS_ENUM的作用是在使用typedef的同時,確定枚舉的底層數據類型,這個效果是enum所達不到的。NS_OPTIONS的作用是,在NS_ENUM的基礎上,使得按位或運算的結果能夠返回正確的數據類型。

所以,凡是需要以按位或操作來進行組合的枚舉都需要用NS_OPTIONS定義,若不需要組合,則可以使用NS_ENUM。


文中如有錯誤,歡迎指正。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容