NS_ENUM和NS_OPTIONS

碰到問題

最近在看一本書奏属,書名叫《Effective Objective-C 2.0 編寫高質(zhì)量iOS與OS X代碼的52個有效方法》,其中談及了NS_ENUMNS_OPTIONS的區(qū)別,不是很能理解,直接上手來探究一番。

探究問題

使用場景

NS_ENUM通常用在單一侠鳄、不可組合的狀態(tài),例如狀態(tài)欄的樣式

typedef NS_ENUM(NSInteger, UIStatusBarStyle) {
    UIStatusBarStyleDefault                                     = 0, // Dark content, for use on light backgrounds
    UIStatusBarStyleLightContent     NS_ENUM_AVAILABLE_IOS(7_0) = 1, // Light content, for use on dark backgrounds
    
    UIStatusBarStyleBlackTranslucent NS_ENUM_DEPRECATED_IOS(2_0, 7_0, "Use UIStatusBarStyleLightContent") = 1,
    UIStatusBarStyleBlackOpaque      NS_ENUM_DEPRECATED_IOS(2_0, 7_0, "Use UIStatusBarStyleLightContent") = 2,
} __TVOS_PROHIBITED;

NS_OPTIONS則用在非單一死宣、可組合的狀態(tài)伟恶,例如屏幕方向

typedef NS_OPTIONS(NSUInteger, UIInterfaceOrientationMask) {
    UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
    UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
    UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
    UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
    UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
    UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
    UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
} __TVOS_PROHIBITED;

宏定義

在此認識的基礎(chǔ)上,來看一下NS_ENUMNS_OPTIONS這兩個宏的定義分別是什么毅该,

#define NS_ENUM(...) CF_ENUM(__VA_ARGS__)
#define NS_OPTIONS(_type, _name) CF_OPTIONS(_type, _name)

再看看CF_ENUMCF_OPTIONS分別是什么博秫,CF_ENUM中間還多了一層,先來看一下CF_ENUM的定義眶掌,

#define CF_ENUM(...) __CF_ENUM_GET_MACRO(__VA_ARGS__, __CF_NAMED_ENUM, __CF_ANON_ENUM, )(__VA_ARGS__)

結(jié)合__CF_ENUM_GET_MACRO的定義來看,

#define __CF_ENUM_GET_MACRO(_1, _2, NAME, ...) NAME

不難理解挡育,當(dāng)CF_ENUM只有一個入?yún)⒌臅r候,等同于__CF_ANON_ENUM(__VA_ARGS__)朴爬,有兩個入?yún)⒌臅r候即寒,則等同于__CF_NAMED_ENUM(__VA_ARGS__),很快就能找這兩個宏的定義,

#if (__cplusplus && __cplusplus >= 201103L && (__has_extension(cxx_strong_enums) || __has_feature(objc_fixed_enum))) || (!__cplusplus && __has_feature(objc_fixed_enum))
#define __CF_NAMED_ENUM(_type, _name)     enum __CF_ENUM_ATTRIBUTES _name : _type _name; enum _name : _type
#define __CF_ANON_ENUM(_type)             enum __CF_ENUM_ATTRIBUTES : _type
#if (__cplusplus)
#define CF_OPTIONS(_type, _name) _type _name; enum __CF_OPTIONS_ATTRIBUTES : _type
#else
#define CF_OPTIONS(_type, _name) enum __CF_OPTIONS_ATTRIBUTES _name : _type _name; enum _name : _type
#endif
#else
#define __CF_NAMED_ENUM(_type, _name) _type _name; enum
#define __CF_ANON_ENUM(_type) enum
#define CF_OPTIONS(_type, _name) _type _name; enum
#endif

發(fā)現(xiàn)CF_OPTIONS的定義也在這里母赵,其中第一個#if的內(nèi)容并未深究逸爵,按書中所說,是用來判斷編譯器是否支持新的枚舉定義方式凹嘲。
可以發(fā)現(xiàn)师倔,CF_OPTIONS__CF_NAMED_ENUM兩者在__cplusplusNO的時候定義是一樣的,為YES周蹭,定義才不一樣趋艘,按書上所說原因是:

在用或運算操作兩個枚舉值時,C++認為運算結(jié)果的數(shù)據(jù)類型應(yīng)該是枚舉的底層數(shù)據(jù)類型凶朗,也就是NSUInteger瓷胧。而且C++不允許將這個底層類型“隱式轉(zhuǎn)換”為枚舉類型本身。

驗證一下

在.m文件中添加如下代碼棚愤,

typedef NS_ENUM(NSUInteger, MyType) {
    MyType1,
    MyType2,
    MyType3,
};
typedef NS_OPTIONS(NSUInteger, MyState){
    MyState1 = 1 << 0,
    MyState2 = 1 << 1,
    MyState3 = 1 << 2,
};

任意一處添加如下代碼搓萧,并編譯,

MyType type;
MyState state;

type = 1;                       //no warning
type = MyType1 | MyType2;       //no warning

state = 1;                      //no warning
state = MyState2 | MyState3;    //no warning

將.m文件改成.mm文件遇八,再次編譯,

MyType type;
MyState state;

type = 1;                       //warning
type = MyType1 | MyType2;       //warning

state = 1;                      //no warning
state = MyState2 | MyState3;    //no warning

由此可見耍休,用NS_ENUM定義的枚舉刃永,在C++模式下,在碰到需要數(shù)據(jù)類型進行隱式轉(zhuǎn)換的時候羊精,就會報錯斯够,通過Product--Perform Action--Preprocess來看一下兩者在C++編譯模式下的區(qū)別,

typedef enum __attribute__((enum_extensibility(open))) MyType : NSUInteger MyType; enum MyType : NSUInteger {
    MyType1,
    MyType2,
    MyType3,
};
typedef NSUInteger MyState; enum __attribute__((flag_enum,enum_extensibility(open))) : NSUInteger{
    MyState1 = 1 << 0,
    MyState2 = 1 << 1,
    MyState3 = 1 << 2,
};

可以看出喧锦,MyState是由一個NSUInteger數(shù)據(jù)類型typedef來的读规,而不是一個枚舉類型,以這種方式燃少,避免了“C++不允許數(shù)據(jù)類型隱式轉(zhuǎn)換”的問題束亏。

enum_extensibility

在preprocess出來的代碼中,發(fā)現(xiàn)了__attribute__((enum_extensibility(open)))阵具,這個是干嘛用的碍遍,于是我查了一下資料,關(guān)于__attribute__的黑魔法阳液,可以閱讀一下文章結(jié)尾給出的一些鏈接怕敬,此處針對enum_extensibility做一些研究。

Xcode默認的編譯器是Clang帘皿,在Clang的文檔中东跪,我查到了enum_extensibility的解釋。

Attribute enum_extensibility is used to distinguish between enum definitions that are extensible and those that are not. The attribute can take either closed or open as an argument. closed indicates a variable of the enum type takes a value that corresponds to one of the enumerators listed in the enum definition or, when the enum is annotated with flag_enum, a value that can be constructed using values corresponding to the enumerators. open indicates a variable of the enum type can take any values allowed by the standard and instructs clang to be more lenient when issuing warnings.

簡而言之,就是open的時候允許類型隱式轉(zhuǎn)換虽填,closed的時候不允許類型隱式轉(zhuǎn)換丁恭,并給出了例子,如下卤唉,

enum __attribute__((enum_extensibility(closed))) ClosedEnum {
  A0, A1
};

enum __attribute__((enum_extensibility(open))) OpenEnum {
  B0, B1
};

enum __attribute__((enum_extensibility(closed),flag_enum)) ClosedFlagEnum {
  C0 = 1 << 0, C1 = 1 << 1
};

enum __attribute__((enum_extensibility(open),flag_enum)) OpenFlagEnum {
  D0 = 1 << 0, D1 = 1 << 1
};

void foo1() {
  enum ClosedEnum ce;
  enum OpenEnum oe;
  enum ClosedFlagEnum cfe;
  enum OpenFlagEnum ofe;

  ce = A1;           // no warnings
  ce = 100;          // warning issued
  oe = B1;           // no warnings
  oe = 100;          // no warnings
  cfe = C0 | C1;     // no warnings
  cfe = C0 | C1 | 4; // warning issued
  ofe = D0 | D1;     // no warnings
  ofe = D0 | D1 | 4; // no warnings
}

代碼拷貝進.m文件涩惑,編譯,都不報錯桑驱;
將.m改成.mm竭恬,編譯,除了ce = A1;oe = B1;熬的,其他都報錯痊硕;
跟例子里說的情況都不吻合,不知道是我理解錯誤押框,還是哪里不對岔绸,希望有看到的大神可以教教我。

總結(jié)

當(dāng)需要定義單一橡伞、不可組合的枚舉類型時盒揉,使用NS_ENUM,當(dāng)需要定義非單一兑徘、可組合的枚舉行勒時刚盈,使用NS_OPTIONS,這樣使用挂脑,在任何編譯模式下應(yīng)該都不會出錯藕漱。

對碰到的其他問題一知半解,主要原因是對各方面了解的比較少崭闲,還需要繼續(xù)努力學(xué)習(xí)肋联。

相關(guān)文章

黑魔法attribute((cleanup))
attribute 總結(jié)
Clang Attributes 黑魔法小記

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市刁俭,隨后出現(xiàn)的幾起案子橄仍,更是在濱河造成了極大的恐慌,老刑警劉巖牍戚,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沙兰,死亡現(xiàn)場離奇詭異,居然都是意外死亡翘魄,警方通過查閱死者的電腦和手機鼎天,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來暑竟,“玉大人斋射,你說我怎么就攤上這事育勺。” “怎么了罗岖?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵涧至,是天一觀的道長。 經(jīng)常有香客問我桑包,道長南蓬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任哑了,我火速辦了婚禮赘方,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弱左。我一直安慰自己窄陡,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布拆火。 她就那樣靜靜地躺著跳夭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪们镜。 梳的紋絲不亂的頭發(fā)上币叹,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機與錄音模狭,去河邊找鬼颈抚。 笑死,一個胖子當(dāng)著我的面吹牛胞皱,可吹牛的內(nèi)容都是我干的邪意。 我是一名探鬼主播九妈,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼反砌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了萌朱?” 一聲冷哼從身側(cè)響起宴树,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎晶疼,沒想到半個月后酒贬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡翠霍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年锭吨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寒匙。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡零如,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情考蕾,我是刑警寧澤祸憋,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站肖卧,受9級特大地震影響蚯窥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜塞帐,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一拦赠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧壁榕,春花似錦矛紫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至牡辽,卻和暖如春喳篇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背态辛。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工麸澜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奏黑。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓炊邦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親熟史。 傳聞我的和親對象是個殘疾皇子馁害,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

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