在iOS开发中枚举大家用得最多的应该是NS_ENUM。NS_ENUM也没什么好讲的。主要来讲讲位枚举NS_OPTIONS。
下面是他们在Foundation.framework的NSObjCRuntime.h的定义
#if (__cplusplus && __cplusplus >= 201103L && (__has_extension(cxx_strong_enums) || __has_feature(objc_fixed_enum))) || (!__cplusplus && __has_feature(objc_fixed_enum))
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
#if (__cplusplus)
#define NS_OPTIONS(_type, _name) _type _name; enum : _type
#else
#define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
#endif
#else
#define NS_ENUM(_type, _name) _type _name; enum
#define NS_OPTIONS(_type, _name) _type _name; enum
#endif
其实从枚举定义来看,NS_ENUM和NS_OPTIONS本质是一样的,仅仅从字面上来区分其用途。NS_ENUM是通用情况,NS_OPTIONS一般用来定义具有位移操作或特点的情况(bitmask掩码)。
与、或操作
先讲讲关于二进制的位于操作。可能好多的同学都有些忘了。这里复习下。
位操作 | 解释 | 例子 | 应用 |
---|---|---|---|
按位与(&) | 有0则0,当俩位同时为1时返回1. | 4=0000 0000 0000 0100 &7 =0000 0000 0000 0111= 0000 0000 0000 0100 | 1.清零 2.获取一个数据的指定位 3.保留数据区的特定位 |
按位或(I) | 有1则1,只要有一位为1则即可返回1. | 5 = 0000 0000 0000 0101I 7= 0000 0000 0000 0111=0000 0000 0000 0111 |
设定一个数据的指定位 |
<<:左移运算符。左移k位 相当于 *2^k
>>:右移运算符。右移k位 相当于/2^k
实例
NS_OPTIONS位枚举的特点是可以使用位运算来处理枚举值,实际使用中可以用一个变量存储多个枚举值,表示互不影响的多个设置。比如系统中的UIViewAutoresizing定义如下。
enum UIViewAutoresizing {
UIViewAutoresizingNone = 0, // 000000
UIViewAutoresizingFlexibleLeftMargin = 1 << 0, // 000001
UIViewAutoresizingFlexibleWidth = 1 << 1,// 000010
UIViewAutoresizingFlexibleRightMargin = 1 << 2,// 000100
UIViewAutoresizingFlexibleTopMargin = 1 << 3,// 001000
UIViewAutoresizingFlexibleHeight = 1 << 4,// 010000
UIViewAutoresizingFlexibleBottomMargin = 1 << 5,// 100000
}
为了说明问题这里就用UIViewAutoresizing举个例子。上面把对应的二进制写在了后面。现在要实现视图的宽和高自适应,UIViewAutoResizingFlexibleWidth| UIViewAutoresizingFlexibleHeight。
。
一般会这样写:
UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
转换为二进制计算一下。
UIViewAutoresizing resizing = 000010 |010000 = 010010
这样就实现了变量保存多个枚举值。那么如何判断变量是否包含某个枚举值了,这的通过与操作。比如我要判断是否包含了UIViewAutoresizingFlexibleWidth
if (resizing & UIViewAutoresizingFlexibleWidth) {
// UIViewAutoresizingFlexibleWidth is set
}
转换为二进制:
if (010010 & 000010) {
// UIViewAutoresizingFlexibleWidth is set
}
010010 & 000010 = 000010 很明显不等于0。所以为YES。那么用一个不包含的试一下。
if (resizing & UIViewAutoresizingFlexibleTopMargin) {
// UIViewAutoresizingFlexibleTopMargin is set
}
转为二进制
if (010010 & 001000) {
// UIViewAutoresizingFlexibleTopMargin is set
}
很明显010010 & 001000 = 0,所以能够知道resizing不包含UIViewAutoresizingFlexibleTopMargin。
通过以上的这种方式实现了一个变量保存多个枚举值。也就是NS_OPTIONS的原理。
需要注意的地方
- 枚举命名方式尽量用系统的风格,比如枚举名为UIViewAutoresizing,具体的值为UIViewAutoresizingNone。简单来讲就是(枚举名+状态)
- 如果要实现一个变量保存多个枚举值就用NS_OPTIONS。个人觉得用NS_OPTIONS完全可以替代NS_ENUM。只需要在使用的时候不用与、或操作就可以了
- 用NS_ENUM与NS_OPTIONS宏来定义枚举类型,并指明其底层数据类型。这样做可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型。
- 在处理枚举类型的switch语句中不要实现default分支。这样的话,加入新枚举之后,编译器就会提示开发者:switch语句并未处理所有枚举。
- 预留一个枚举值,用于扩展或者用于表示没有的情况。