iOS-有效编写高质量Objective-C方法二

关于Effective Objective-C 的阅读分享系列文章,我们继续
在本篇文章中,我将重点介绍枚举类型,枚举在OC中,可以说用到的地方特别多,那我们真正有了解它吗?

由于OC是基于C语言的,所以OC同样拥有枚举功能:enum。

在系统框架中频繁使用到了枚举,然而开发者很容易忽视它,同样我也老是忽视它。

什么地方用到了枚举?

在以一系列常量来表示错误状态码或可选组合时,非常适合使用枚举来为其命名。

枚举只是一种常量的命名方式。某个对象经历的各种状态就可以定义为一个简单的枚举。

简单枚举

比如说,我们用下列枚举表示网络状态的连接:

1
2
3
4
5
enum PPSConnectionState{
PPSConnectionStateDisconnected,
PPSConnectionStateConnecting,
PPSConnectionStateConnected,
};

由于每个枚举的命名都能够让我们轻易理解它代表的含义,这样的代码才更易读懂。编译器会为每个枚举分配一个独有的编号,从0开始,每个枚举递增1.实现枚举所用的数据类型取决于编译器,不过二进制位的个数必须能够完全表示下列枚举编号才行。

但是我们如果像上面那样定义了枚举,那么我们定义一个枚举变量就需要像下面这样写:

1
enum PPSConnectionState state = PPSConnectionStateConnected;

我们每次都需要敲入 enum 这样不免有些繁琐 我们要像一般的对象那样直接敲PPSConnectionState 如果要这样实现的话 那我们定义枚举需要使用typedef关键字

1
2
3
4
5
6
enum PPSConnectionState {
PPSConnectionStateDisconnected,
PPSConnectionStateConnecting,
PPSConnectionStateConnected,
};
typedef enum PPSConnectionState PPSConnectionState;

现在就可以简写:

1
PPSConnectionState state = PPSConnectionStateConnected;

在C++11 标准修订了枚举的一些特性,有一项比较实用的是 我们在定义枚举时,可以定义枚举的底层数据类型(编译器用来表示枚举的编号) 这样的话 我们就能够使用向前声明枚举变量了。

1
2
3
4
5
6
7
8
9
10
11
12
enum PPSConnectionState : NSInteger{
PPSConnectionStateDisconnected,
PPSConnectionStateConnecting,
PPSConnectionStateConnected,
};

//也可以直接使用
enum PPSConnectionState {
PPSConnectionStateDisconnected = 0,
PPSConnectionStateConnecting,
PPSConnectionStateConnected,
};

上面两种方式定义出来的枚举 底层数据类型是一样的 默认第一种方式 编译器默认分配是从0开始,如果我们想要让我们定义的枚举直接从1开始,那么我们可以使用第二种方式,直接将0变成1,那我们的枚举就是从1开始的。

组合枚举

在使用系统的框架时,我们经常能碰到组合枚举,特别是在使用UIKit时,例如:

1
2
3
4
5
6
7
enum UIViewAutoresizing{
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
...
};

像上面的枚举,表示的是某个视图应该如何在水平和垂直方向上调整大小,
我们看到他们的底层数据 是使用二进制值 这样做的目的是为了将枚举组合使用,例如我们可以定义一个枚举:

1
enum UIViewAutoresizing resizing = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth;

这样两个枚举就组合起来了

在Foundation框架中,定义了一些辅助的宏,在定义枚举时,我们经常使用这些宏来指定枚举的底层数据

1
2
3
4
5
typedef NS_ENUM(NSUInteger, PPSConnectionState){
PPSConnectionStateDisconnected,
PPSConnectionStateConnecting,
PPSConnectionStateConnected,
};
1
2
3
4
5
6
7
typedef NS_OPTION(NSUInteger, UIViewAutoresizing){
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
...
};

我们看到上面有两种方式的宏,那么我们应该怎样来选择呢

凡是需要以位或操作来组合枚举的都应使用NS_OPTION来定义

凡是枚举不需要组合,都应该使用NS_ENUM来定义

注意

在switch语句中,我们经常用来判断枚举类型,但是我们总喜欢在switch语句中加上default分支,如果我们是用来判断枚举的话,最好不要加上default分支,因为如果我们在枚举中又加上了一种状态,那么我们就需要去找到每一个switch语句去处理,不然会直接走到default分支,编译器也会直接运行,不会有任何提示。但是如果没有default分支,在我们增加枚举类型时,如果我们不修改switch,那么在编译时,编译器会报出警告,告知我们枚举没有判断完全。

总结

  • 我们总是应该使用枚举来表示状态机(某个对象)的状态,我们要注意枚举的命名。
  • 在定义枚举时,如果可以多个枚举组合,那么枚举的底层数据,应该是使用二进制值来表示,2的幂。
  • 在使用NS_ENUM NS_OPTION来定义枚举类型时,我们可以直接指定底层数据,这样做可以确保枚举是我们开发者想要的底层数据。
  • 在switch语句判断枚举时,不要实现default语句,这样的话在新增枚举类型时,可以触发编译器报警