OC 严格枚举

通常我们在使用枚举时会使用 default 处理所有额外的case,防止遇到没有枚举情况导致崩溃。有一些编程规范会建议列举出所有的 case,避免枚举成员增加之后忘记没有随之增加新的 case 处理逻辑导致的 bug,通常这种 bug 也不太容易发现。为了避免某些重要的枚举漏掉处理 case,我们可以使用宏和编译特性保证这一点

正常的枚举

我们从一个简单的枚举使用例子开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef enum : NSUInteger {
EnumCaseA,
EnumCaseB,
EnumCaseC,
EnumCaseD
} EnumCase;


void useEnumDefault() {
EnumCase val = EnumCaseA;
switch (val) {
case EnumCaseA:
// EnumCaseA handler
break;
case EnumCaseB:
// EnumCaseB handler
break;
default:
break;
}
}

由于使用了 default,即使我们没有列举出剩下的 EnumCaseC & EnumCaseD 也没有任何问题;当然,再在枚举中增加一个 EnumCaseE 依然不会有错误。

但是如果这样的枚举使用的位置很多,而新的 EnumCaseE 需要单独的处理逻辑的话,就很容易漏掉对其中某一个 EnumCaseE 的处理,编译器也不会给你任何的信息提示

-Wswitch-enum 编译检查

在 GCC 文档中找到一个编译检查项符合要求;当开启这个编译检查时,必须列出所有的枚举 case,否则就会出现错误。即使用了 default 也不行

当在 Xcode 中添加这个编译选项之后,可能会发现一串的错误。因为在 Xcode 中开启意味着所有的枚举都要遵守这个规则。但我们只是想要某些重要的枚举才需要这样的检查

clang diagnostic

Clang diagnostic 是 Clang 编译时的一个检查上下文声明;相信很多人都用来关闭过一些警告:

1
2
3
4
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
NSInteger val = 1;
#pragma clang diagnostic pop

它的原理是 Clang 在检查时会开启一个上下文,上下文中是全局的检查配置参数;所有的警告和错误都是通过这些参数来指定的。

如果你需要在某个代码块时使用一些临时的检查配置,那么就可以使用 #pragma clang diagnostic push 开启一个新的上下文,然后通过 #pragma clang diagnostic pop 关闭这个上下文。

期间可以通过 #pragma clang diagnostic (ignored|note|error|...) 声明一些新的配置

严格枚举实现

基于上面的背景知识,我们可以通过宏定义一个严格的枚举

1
2
3
4
5
6
7
8
#define STRICT_SWITCH_BEGIN(val) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic error \"-Wswitch-enum\"") \
switch(val) {

#define STRICT_SWITCH_END \
} \
_Pragma("clang diagnostic pop")

由于宏不能嵌套,所以这儿需要使用 _Pragma() 代替 #pragma

然后我们再来看看使用效果

oc strict enum

大功告成,编译器错误并且提示 EnumCaseC & EnumCaseD 没有被处理

额外

可能有小伙伴会问:如果好几个 case 要用同一段逻辑,难道也要写好几遍吗?是~也不是,如果有共同的逻辑的话,还是需要将所有 case 全部列举出来,但是可以利用 switch 的穿透,使多个 case 执行同一段逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void useStrictDefault() {
EnumCase val = EnumCaseA;
STRICT_SWITCH_BEGIN(val)
case EnumCaseA:
// EnumCaseA handler
break;
case EnumCaseB:
// EnumCaseB handler
break;
case EnumCaseC:
case EnumCaseD:
// Common handler
break;
STRICT_SWITCH_END
}

EnumCaseC & EnumCaseD 共用了一段处理逻辑,

参考

GCC 编译警告

Clang

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2017-2021 HonQi

请我喝杯咖啡吧~