OC枚舉值到字符串轉換
Swift枚舉類型引入了Raw Value的概念,每個枚舉case的Raw Value可以是其case name。假設有case king,則king.rawValue = "king"。相比之下OC枚舉類型就弱爆了,只能綁定整數;要"反射"自己的case name,必須手動實現反射函數。不過,宏替換可以在一定程度上幫助我們自動實現。
本文基于Easy way to use variables of enum types as string in C?
,是筆者無意間看到的。短短幾十行宏代碼,看了好大一會兒也不知所以然,不得不重新捧起K&R復習Marcos;晚上回家接著細細研讀,不禁感嘆:竟然還有這種操作!
A. 使用
這套宏的用法分為兩步:
A.1 在.h文件中定義枚舉,聲明反射函數
// M1
#define RAP_DIRECTION(XX) \
XX(RAPDirectionEast, ) \
XX(RAPDirectionSouth, ) \
XX(RAPDirectionWest, = 50) \
XX(RAPDirectionNorth, = 100) \
// M2
DECLARE_ENUM(RAPDirection, RAP_DIRECTION)
- M1生成多個枚舉case。枚舉case的定義格式為XX(name, assign),name即case name;assign即對應整數值,格式必須為
= Integer
,不填表示使用默認值; - M2定義枚舉,聲明反射函數。反射函數的有兩個:
- 根據case獲取case name:NSStringFromRAPDirection(RAPDirection value);
- 根據case name獲取case:RAPDirectionFromNSString(NSString *string);
A.2 在.m文件中實現反射函數
DEFINE_ENUM(RAPDirection, RAP_DIRECTION)
- RAPDirection和RAP_DIRECTION要與.h中的宏對應。RAPDirection是枚舉類型名稱,RAP_DIRECTION是宏,展開后變成多個枚舉case。
A.3 例子
-
在任意.h文件中定義枚舉,聲明反射函數:
#import "EnumMarcos.h" #define RAP_DIRECTION(XX) \ XX(RAPDirectionEast, ) \ XX(RAPDirectionSouth, ) \ XX(RAPDirectionWest, = 50) \ XX(RAPDirectionNorth, = 100) \ DECLARE_ENUM(RAPDirection, RAP_DIRECTION)
-
在相應的.m文件中實現反射函數:
DEFINE_ENUM(RAPDirection, RAP_DIRECTION)
-
嘗試使用:
NSString *str = NSStringFromRAPDirection(RAPDirectionEast); NSLog(@"RAPDirectionEast has case name: %@", str); RAPDirection dir = RAPDirectionFromNSString(@"RAPDirectionNorth"); NSLog(@"RAPDirectionNorth has case value: %zd", dir);
輸出結果:
RAPDirectionEast has case name: RAPDirectionEast
RAPDirectionNorth has case value: 100
B. 實現
B.1 DECLARE_ENUM(EnumType, ENUM_DEF)
#define DECLARE_ENUM(EnumType, ENUM_DEF) \
typedef NS_ENUM(NSInteger, EnumType) { \
ENUM_DEF(ENUM_VALUE) \
}; \
NSString *NSStringFrom##EnumType(EnumType value); \
EnumType EnumType##FromNSString(NSString *string); \
- 第1行:EnumType是枚舉類型;ENUM_DEF是替換宏,格式為XX(name, assign),同同ENUM_VALUE,ENUM_CASE以及ENUM_STRCMP的形式一致;
- 第2行:Apple式的枚舉定義風格;
- 第3行:ENUM_DEF(ENUM_VALUE)展開為多個ENUM_VALUE,進一步展開為多個
name assign,
的形式;(ENUM_VALUE的分析在下面) - 第5,6行:聲明反射函數。##的用法不再贅述;
B.1.1 ENUM_VALUE(name, assign)
#define ENUM_VALUE(name, assign) name assign,
- 定義一個枚舉case:name即case name;assign即整數值,格式為
= Interfer
。 - 例如:ENUM_VALUE(King, = 13);展開后:
King = 13,
,注意最后的逗號不可獲取。
B.2 DEFINE_ENUM(EnumType, ENUM_DEF)
#define DEFINE_ENUM(EnumType, ENUM_DEF) \
NSString *NSStringFrom##EnumType(EnumType value) \
{ \
switch(value) \
{ \
ENUM_DEF(ENUM_CASE) \
default: return @""; \
} \
} \
EnumType EnumType##FromNSString(NSString *string) \
{ \
ENUM_DEF(ENUM_STRCMP) \
return (EnumType)0; \
}
- 第7行:如果找不到對應case name,返回@"";
- 第8行:如果找不到對應case,則返回0;
- 其他同DECLARE_ENUM(EnumType, ENUM_DEF);
B.2.1 ENUM_CASE(name, assign)
#define ENUM_CASE(name, assign) case name: return @#name;
- 展開后是一個switch語句的case,返回一個字符串;#name表示"name",所以@#name表示@"name",OC字符串。
- 例如:ENUM_CASE(King, 13),展開后:case King: return @"King";
B.2.1 ENUM_STRCMP(name, assign)
#define ENUM_STRCMP(name, assign) if ([string isEqualToString:@#name]) return name;
- 展開后通過比較字符串,返回相應枚舉case;
- string是函數
EnumType##FromNSString
的參數,展開時自動填入; - 例如:ENUM_STRCMP(King, 13),展開后:if ([string isEqualToString:@"King"]) return King;