對于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。
文中如有錯誤,歡迎指正。