利用宏定义实现枚举转字符串

前言

在我们编码过程中,枚举会经常用到, 尤其是用来表示多种状态时.
然而, 在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. 在某一个类的空间内声明定义转换方法, 对于作用域外(其他类)的地方使用非常不便.
  2. 当对枚举的标识符进行增删改操作时, 必须也要同时修改转换方法内的代码, 非常不灵活.

优化

优化问题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;
}

有两点需要解释:

  1. 使用static可以防止发生函数重复声明定义的错误. (使用NS_INLINE也可以.)
  2. __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

灵感来自 https://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types-as-string-in-c/202511#202511

Demo https://github.com/onekyle/EnumStringConvert/tree/master

如果你有更好的想法 请不吝赐教.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,152评论 0 4
  • · 事情经过 昨天傍晚因为女儿在学校的事情而恼火,情绪一度跌到谷底。一半恼的是女儿的情商,一半恼的是告状的家长那一...
    玄月之佑阅读 1,043评论 5 4
  • 这个月开始读《硅谷钢铁侠》,书读到一半,就开始走神了。因为马斯克的前妻贾斯汀触动了我,她和马斯克有良好的感情基础,...
    熊熊如焰阅读 381评论 0 1
  • 我常常幻想自己在一个夕阳西下的海边惬意享受自然的美景,或者在一个山清水秀的桃花源呼吸新鲜空气,或者在奔驰的列车上聆...
    可可咳咳阅读 256评论 0 0