前言
在我们编码过程中,枚举
会经常用到, 尤其是用来表示多种状态时.
然而, 在OC中, 对枚举
进行打印调试 或者 拼接方法 的操作的编程体验是非常差的.
例子
以下这种情况你应该会经常遇到:
// 你工作时的几种状态
typedef NS_ENUM(NSInteger, WorkStatus) {
/** 摸鱼*/
WorkStatusUnKnown,
/** 认真工作*/
WorkStatusWorking,
/** 休息*/
WorkStatusSleeping,
};
当我们想要打印这个枚举时, 默认输出的是这个枚举标识符所对应的值, 这样的效果是�非常不理想的, 只输出数字,不直观(我们之所以使用枚举来定义状态, 不就是要直观的表示吗?), 为了达到直观
这一目的, 写一个将�枚举标识符转换成字符串的方法就势在必得了:
- (NSString *)WorkStatusDescription:(WorkStatus)status
{
NSString *desc = nil;
switch (status) {
case WorkStatusUnKnown:
desc = @"WorkStatusUnKnown";
break;
case WorkStatusWorking:
desc = @"WorkStatusWorking";
break;
case WorkStatusSleeping:
desc = @"WorkStatusSleeping";
break;
default:
desc = @"NoOne";
break;
}
return desc;
}
问题
这样操作, 也许解决了不直观的问题, 但是细看之下还是有两点很大的问题.
- 在某一个类的空间内声明定义转换方法, 对于作用域外(其他类)的地方使用非常不便.
- 当对枚举的标识符进行增删改操作时, 必须也要同时修改
转换方法
内的代码, 非常不灵活.
优化
优化问题1
针对于 问题 1, 我们可以通过在头文件中声明定义函数来解决:
static NSString * WorkStatusDescription(WorkStatus status) __attribute__((unused));
static NSString * WorkStatusDescription(WorkStatus status) {
NSString *desc = nil;
switch (status) {
case WorkStatusUnKnown:
desc = @"WorkStatusUnKnown";
break;
case WorkStatusWorking:
desc = @"WorkStatusWorking";
break;
case WorkStatusSleeping:
desc = @"WorkStatusSleeping";
break;
default:
desc = @"NoOne";
break;
}
return desc;
}
有两点需要解释:
- 使用
static
可以防止发生函数重复声明定义的错误. (使用NS_INLINE
也可以.) -
__attribute__((unused))
表示告诉编译器忽略Unused Warning
.
优化问题2
解决 问题 2 的关键在于如何将一个枚举标识符灵活的转换成字符串. 根据这个思路, 很自然的就可以联想到 使用 宏定义中 #
可以将参数转换成字符串的特性来解决.
// 定义枚举标识符和其对应的值的宏
#define ENUM_VALUE(name,assign) name assign,
// �将枚举标识符转换成字符串的宏
#define ENUM_CASE(name,assign) case name: return @#name;
// 将字符串转换为枚举标识符的宏
#define ENUM_STRCMP(name,assign) if ([string isEqualToString:@#name]) return name;
/// 声明函数 及 定义枚举
#define DECLARE_ENUM(EnumType,ENUM_DEF) \
typedef NS_ENUM(NSUInteger, EnumType) { \
ENUM_DEF(ENUM_VALUE) \
}; \
static NSString *stringFrom##EnumType(EnumType value) __attribute__((unused)); \
static EnumType EnumType##FromString(NSString *string) __attribute__((unused)); \
static NSString *stringFrom##EnumType(EnumType value) { \
switch(value) { \
ENUM_DEF(ENUM_CASE) \
default: return @""; \
} \
} \
\
static EnumType EnumType##FromString(NSString *string) { \
ENUM_DEF(ENUM_STRCMP) \
return (EnumType)0; \
}
为了一气呵成, 已经将针对于 问题 1
优化合并到上面这个代码块中.
使用
// 导入定义宏所在的头文件.
#import "enum_generator.h"
// 使用定义的宏声明枚举
#define WorkStatus(XX) \
XX(WorkStatusUnKnown,) \
XX(WorkStatusWorking,) \
XX(WorkStatusSleeping,=50)
// 生成定义的枚举 与 转换方法.
DECLARE_ENUM(WorkStatus,WorkStatus)
为了更直观的感受, 我们进入预编译阶段, 查看宏生成的代码(为了看起来清晰 已经进行手动换行):
// DECLARE_ENUM(WorkStatus,WorkStatus) 所生成的代码
typedef enum WorkStatus : NSUInteger WorkStatus; enum WorkStatus : NSUInteger {
WorkStatusUnKnown ,
WorkStatusWorking ,
WorkStatusSleeping =50,
};
static NSString *stringFromWorkStatu(WorkStatus value) __attribute__((unused));
static WorkStatus WorkStatusFromString(NSString *string) __attribute__((unused));
static NSString *stringFromWorkStatus(WorkStatus value) {
switch(value) {
case WorkStatusUnKnown:
return @"WorkStatusUnKnown";
case WorkStatusWorking:
return @"WorkStatusWorking";
case WorkStatusSleeping:
return @"WorkStatusSleeping";
default:
return @"";
}
}
static WorkStatus WorkStatusFromString(NSString *string) {
if ([string isEqualToString:@"WorkStatusUnKnown"]) return WorkStatusUnKnown;
if ([string isEqualToString:@"WorkStatusWorking"]) return WorkStatusWorking;
if ([string isEqualToString:@"WorkStatusSleeping"]) return WorkStatusSleeping;
return (WorkStatus)0;
}
测试
WorkStatus testWorkStatus = WorkStatusUnKnown;
NSLog(@"workstatus is: %@", stringFromWorkStatus(testWorkStatus));
if (testWorkStatus == WorkStatusFromString(@"WorkStatusUnKnown")) {
NSLog(@"确认在摸鱼");
}
// 输出:
// workstatus is: WorkStatusUnKnown
// 确认在摸鱼
More
Demo https://github.com/onekyle/EnumStringConvert/tree/master
如果你有更好的想法 请不吝赐教.