当先锋百科网

首页 1 2 3 4 5 6 7

第5条:用枚举表示状态、选项、状态码

1. 枚举类型的定义

枚举只是一种常量命名方式。某个对象所经历的各种状态就可以定义为一个简单的枚举集(编译器会为枚举集里的枚举各自分配一个独有的编号,从0开始,每个枚举递增1)。
然而,定义枚举变量的方式并不简洁:

// 声明一个名为EOCConnectionState的枚举
enum EOCConnectionState{
    EOCConnectionStateDisconnected,
    EOCConnectionStateConnecting,
    EOCConnectionStateConnected,
};
// 定义一个名为state的枚举变量
enum EOCConnectionState state = EOCConnectionStatedDisconnected;

可以通过typedef关键字重新定义枚举类型:

// 声明一个名为EOCConnectionState的枚举
enum EOCConnectionState{
    EOCConnectionStateDisconnected,
    EOCConnectionStateConnecting,
    EOCConnectionStateConnected,
};
// 通过typedef给enum EOCConnectionState起别名EOCConnectionState
typedef enum EOCConnectionState EOCConnectionState;
// 定义一个名为state的枚举变量
EOCConnectionState state = EOCConnectionStateDisconnected;

 

2. 指定枚举的底层数据类型

通过指定枚举的底层数据类型,可以向前声明枚举类型。如果编译器不清楚底层数据类型的大小,则在用到此枚举类型时,就不知道究竟该给变量分配多少空间。

*** 指定底层数据类型的语法 ***

enum EOCConnectionStateConnectionState : NSInteger {}; 

3. 枚举类型的其他用法

*** 3.1 手动指定某个枚举成员所对应的值 ***

enum EOCConnectionStateConnectionState {
    EOCConnectionStateDisconnected = 1,
    EOCConnectionStateConnecting,
    EOCConnectionStateConnected,
}; 

EOCConnectionStatedDisconnected的值设为1,此后每个枚举的值递增1。

*** 3.2 选项 ***

/*  UI框架中的UIViewAutoresizing枚举类型  */
enum {
   UIViewAutoresizingNone                 = 0,
   UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
   UIViewAutoresizingFlexibleWidth        = 1 << 1,
   UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
   UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
   UIViewAutoresizingFlexibleHeight       = 1 << 4,
   UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
typedef NSUInteger UIViewAutoresizing;

/*  UI框架中的UIInterfaceOrientationMask枚举类型  */
typedef enum : NSUInteger {
   UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait ),
   UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft ),
   UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight ),
   UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown ),
   UIInterfaceOrientationMaskLandscape =
   (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight ),
   UIInterfaceOrientationMaskAll =
   (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft |
   UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown ),
   UIInterfaceOrientationMaskAllButUpsideDown =
   (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft |
   UIInterfaceOrientationMaskLandscapeRight ),
} UIInterfaceOrientationMask;

 

每个选项均可启用或禁用。用“按位或操作符”(“|”)可组合多个选项,而用“按位与操作符”(“&”)可判断出是否已启用某个选项。

*** 3.3 Foundation框架中辅助的宏——NS_ENUM和NS_OPTIONS ***
Foundation框架中定义了一些辅助的宏,用这些宏来定义枚举类型时,也可以指定用于保存枚举值的底层数据类型。这些宏是用#define预处理指令来定义的,其中,NS_ENUM用于定义像EOCConnectionState这种普通的枚举类型,而NS_OPTIONS用于定义像UIViewAutoresizing这种可以通过“按位或操作符”来组合选项的枚举类型。如:

/* NS_ENUM (所指定的底层数据类型, 枚举类型名称) */
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
};

/* NS_OPTIONS (所指定的底层数据类型, 枚举类型名称) */
typedef NS_OPTIONS(NSUInteger, EOCPermittedDirection) {
    EOCPermittedDirectionUp      = 1 << 0,
    EOCPermittedDirectionDown    = 1 << 1,
    EOCPermittedDirectionLeft    = 1 << 2,
    EOCPermittedDirectionRight   = 1 << 3,
};

 NS_OPTIONS的定义若按C++模式编译,由于作为选项的枚举值经常需要用按位或运算来组合,所以想编译代码,需要将按位或操作的结果显示转换为枚举类型本身(EOCPermittedDirection)。
如:

EOCPermittedDirection permittedDirections = (EOCPermittedDirection)EOCPermittedDirectionUp | EOCPermittedDirectionLeft;

*** 3.4 状态码 ***
可以把逻辑含义相似的一组状态码放入同一个枚举集里,而不用#define预处理指令或常量来定义。

*** 3.5 样式 ***
创建某个UI元素时可以使用不同的样式,在这种情况下,最应该把样式声明为枚举类型。

*** 3.6 在switch语句里 ***

typedef NS_ENUM(NSUInteger, EOCConnectionState){
    EOCConnectionStateDisconnected,
    EOCConnectionStateConnecting,
    EOCConnectionStateConnected,
};

switch (_currentState){
    EOCConnectionStateDisconnected:
        // 实现代码
        break;
    EOCConnectionStateConnecting:
        // 实现代码
        break;
    EOCConnectionStateConnected:
        // 实现代码
        break;
}

 若是用枚举来定义状态机(state machine),则最好不要有default分支,并确保有所有可能出现的情况。

要点

  • 应该用枚举来表示状态机的状态传递给方法的选项以及状态码等值,给这些值起个易懂的名字。
  • 如果把传递给某个方法的选项表示为枚举类型,而多个选项又可同时使用,那么久将各选项值定义为2的幂,以便通过按位或操作将其组合起来。
  • NS_ENUMNS_OPTIONS宏来定义枚举类型,并指明其底层数据类型。这样做可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型。
  • 在处理枚举类型的switch语句中不要实现default分支。这样的话,加入新枚举之后,编译器就会提示开发者:switch语句并未处理所有枚举。