将objective-c typedef转换为等价的string

假设我在.h文件中声明了typedef,如下所示:

typedef enum { JSON, XML, Atom, RSS } FormatType; 

我想build立一个函数,将typedef的数值转换为一个string。 例如,如果发送了消息[self toString:JSON] 它会返回“JSON”。

该函数看起来像这样:

 -(NSString *) toString:(FormatType)formatType { //need help here return []; } 

顺便说一句,如果我尝试这个语法

 [self toString:FormatType.JSON]; 

将typedef值传递给方法,我得到一个错误。 我错过了什么?

这实际上是一个C语言问题,不是特定于Objective-C(它是C语言的超集)。 C中的枚举表示为整数。 所以你需要编写一个返回一个给定枚举值的string的函数。 有很多方法可以做到这一点。 一个string数组,这样的枚举值可以作为一个索引到数组或一个映射结构(例如一个NSDictionary ),将枚举值映射到一个string的工作,但我发现这些方法不如function清晰这使转换显式(和数组的方法,虽然经典的C方法是危险的,如果你的枚举值不是从0 continguous)。 像这样的东西可以工作:

 - (NSString*)formatTypeToString:(FormatType)formatType { NSString *result = nil; switch(formatType) { case JSON: result = @"JSON"; break; case XML: result = @"XML"; break; case Atom: result = @"Atom"; break; case RSS: result = @"RSS"; break; default: [NSException raise:NSGenericException format:@"Unexpected FormatType."]; } return result; } 

您关于枚举值的正确语法的相关问题是您只使用值(例如JSON ),而不是FormatType.JSON组合。 FormatType是一种types,枚举值(例如JSONXML等)是可以分配给该types的值。

你不能轻易做到这一点。 在C和Objective-C中,枚举实际上只是整数常量。 您必须自己生成一个名称表(或者有一些预处理器滥用)。 例如:

 // In a header file typedef enum FormatType { JSON, XML, Atom, RSS } FormatType; extern NSString * const FormatType_toString[]; // In a source file // initialize arrays with explicit indices to make sure // the string match the enums properly NSString * const FormatType_toString[] = { [JSON] = @"JSON", [XML] = @"XML", [Atom] = @"Atom", [RSS] = @"RSS" }; ... // To convert enum to string: NSString *str = FormatType_toString[theEnumValue]; 

这种方法的危险是,如果你改变枚举,你必须记住改变名字的数组。 你可以用一些预处理器的滥用来解决这个问题,但这很棘手和难看。

还要注意,这假定你有一个有效的枚举常量。 如果你有一个不受信任的源的整数值,你还需要检查你的常量是否有效,例如在你的枚举中包含一个“过去最大值”,或者检查它是否小于数组长度, sizeof(FormatType_toString) / sizeof(FormatType_toString[0])

我的解决scheme

编辑:我已经添加了更好的解决scheme,使用Modern Obj-C

1。
将名称作为键放入数组中。
确保索引是适当的枚举, 并按照正确的顺序 (否则是例外)。
注意: names是一个合成为* _names *的属性。

代码没有被检查编译,但我在我的应用程序中使用了相同的技术。

 typedef enum { JSON, XML, Atom, RSS } FormatType; + (NSArray *)names { static NSMutableArray * _names = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _names = [NSMutableArray arrayWithCapacity:4]; [_names insertObject:@"JSON" atIndex:JSON]; [_names insertObject:@"XML" atIndex:XML]; [_names insertObject:@"Atom" atIndex:Atom]; [_names insertObject:@"RSS" atIndex:RSS]; }); return _names; } + (NSString *)nameForType:(FormatType)type { return [[self names] objectAtIndex:type]; } 

//

2。
使用Modern Obj-C,我们可以使用字典将描述与枚举中的键绑定。
顺序没有关系

 typedef NS_ENUM(NSUInteger, UserType) { UserTypeParent = 0, UserTypeStudent = 1, UserTypeTutor = 2, UserTypeUnknown = NSUIntegerMax }; @property (nonatomic) UserType type; + (NSDictionary *)typeDisplayNames { return @{@(UserTypeParent) : @"Parent", @(UserTypeStudent) : @"Student", @(UserTypeTutor) : @"Tutor", @(UserTypeUnknown) : @"Unknown"}; } - (NSString *)typeDisplayName { return [[self class] typeDisplayNames][@(self.type)]; } 

用法(在类实例方法中):

 NSLog(@"%@", [self typeDisplayName]); 

结合@AdamRosenfield的答案,@Christoph评论和另一个处理纯C枚举的技巧,我build议:

 // In a header file typedef enum { JSON = 0, // explicitly indicate starting index XML, Atom, RSS, FormatTypeCount, // keep track of the enum size automatically } FormatType; extern NSString *const FormatTypeName[FormatTypeCount]; // In a source file NSString *const FormatTypeName[FormatTypeCount] = { [JSON] = @"JSON", [XML] = @"XML", [Atom] = @"Atom", [RSS] = @"RSS", }; // Usage NSLog(@"%@", FormatTypeName[XML]); 

在最糟糕的情况下 – 比如如果你改变了枚举,但忘记改变名称数组 – 它将返回零这个键。

在类头中定义typedef枚举:

 typedef enum { IngredientType_text = 0, IngredientType_audio = 1, IngredientType_video = 2, IngredientType_image = 3 } IngredientType; 

在类中写下这样的一个方法:

 + (NSString*)typeStringForType:(IngredientType)_type { NSString *key = [NSString stringWithFormat:@"IngredientType_%i", _type]; return NSLocalizedString(key, nil); } 

Localizable.strings文件中有string:

 /* IngredientType_text */ "IngredientType_0" = "Text"; /* IngredientType_audio */ "IngredientType_1" = "Audio"; /* IngredientType_video */ "IngredientType_2" = "Video"; /* IngredientType_image */ "IngredientType_3" = "Image"; 

我将使用编译器的#string标记(以及macros,使其更紧凑):

 #define ENUM_START \ NSString* ret; \ switch(value) { #define ENUM_CASE(evalue) \ case evalue: \ ret = @#evalue; \ break; #define ENUM_END \ } \ return ret; NSString* _CvtCBCentralManagerStateToString(CBCentralManagerState value) { ENUM_START ENUM_CASE(CBCentralManagerStateUnknown) ENUM_CASE(CBCentralManagerStateResetting) ENUM_CASE(CBCentralManagerStateUnsupported) ENUM_CASE(CBCentralManagerStateUnauthorized) ENUM_CASE(CBCentralManagerStatePoweredOff) ENUM_CASE(CBCentralManagerStatePoweredOn) ENUM_END } 

我喜欢#define这样做的方式:

//将其放置在.h文件的@interface块之外

 typedef enum { JPG, PNG, GIF, PVR } kImageType; #define kImageTypeArray @"JPEG", @"PNG", @"GIF", @"PowerVR", nil // Place this in the .m file, inside the @implementation block // A method to convert an enum to string -(NSString*) imageTypeEnumToString:(kImageType)enumVal { NSArray *imageTypeArray = [[NSArray alloc] initWithObjects:kImageTypeArray]; return [imageTypeArray objectAtIndex:enumVal]; } 

来源 (来源不再可用)

我做了一个混合的解决scheme在这个页面上创build我的,这是一种面向对象的枚举扩展或什么的东西。

事实上,如果你需要的不仅仅是常量(即整数),你可能需要一个模型对象(我们都在谈论MVC,对吧?)

在使用这个之前问一下自己的问题,我是对的吗?实际上,您是否需要一个真正的模型对象,从webservice,plist,SQLite数据库或CoreData初始化?

无论如何,这里的代码(MPI是“我的项目缩写”,大家使用这个或他们的名字,似乎):

MyWonderfulType.h

 typedef NS_ENUM(NSUInteger, MPIMyWonderfulType) { MPIMyWonderfulTypeOne = 1, MPIMyWonderfulTypeTwo = 2, MPIMyWonderfulTypeGreen = 3, MPIMyWonderfulTypeYellow = 4, MPIMyWonderfulTypePumpkin = 5 }; #import <Foundation/Foundation.h> @interface MyWonderfulType : NSObject + (NSString *)displayNameForWonderfulType:(MPIMyWonderfulType)wonderfulType; + (NSString *)urlForWonderfulType:(MPIMyWonderfulType)wonderfulType; @end 

MyWonderfulType.m

 #import "MyWonderfulType.h" @implementation MyWonderfulType + (NSDictionary *)myWonderfulTypeTitles { return @{ @(MPIMyWonderfulTypeOne) : @"One", @(MPIMyWonderfulTypeTwo) : @"Two", @(MPIMyWonderfulTypeGreen) : @"Green", @(MPIMyWonderfulTypeYellow) : @"Yellow", @(MPIMyWonderfulTypePumpkin) : @"Pumpkin" }; } + (NSDictionary *)myWonderfulTypeURLs { return @{ @(MPIMyWonderfulTypeOne) : @"http://www.theone.com", @(MPIMyWonderfulTypeTwo) : @"http://www.thetwo.com", @(MPIMyWonderfulTypeGreen) : @"http://www.thegreen.com", @(MPIMyWonderfulTypeYellow) : @"http://www.theyellow.com", @(MPIMyWonderfulTypePumpkin) : @"http://www.thepumpkin.com" }; } + (NSString *)displayNameForWonderfulType:(MPIMyWonderfulType)wonderfulType { return [MPIMyWonderfulType myWonderfulTypeTitles][@(wonderfulType)]; } + (NSString *)urlForWonderfulType:(MPIMyWonderfulType)wonderfulType { return [MPIMyWonderfulType myWonderfulTypeURLs][@(wonderfulType)]; } @end 

通过删除string依赖关系改进了@ yar1vn答案:

 #define VariableName(arg) (@""#arg) typedef NS_ENUM(NSUInteger, UserType) { UserTypeParent = 0, UserTypeStudent = 1, UserTypeTutor = 2, UserTypeUnknown = NSUIntegerMax }; @property (nonatomic) UserType type; + (NSDictionary *)typeDisplayNames { return @{@(UserTypeParent) : VariableName(UserTypeParent), @(UserTypeStudent) : VariableName(UserTypeStudent), @(UserTypeTutor) : VariableName(UserTypeTutor), @(UserTypeUnknown) : VariableName(UserTypeUnknown)}; } - (NSString *)typeDisplayName { return [[self class] typeDisplayNames][@(self.type)]; } 

因此,当你改变枚举项的名字时,相应的string将被改变。 有用的情况下,如果你不打算给用户显示这个string。

我有一个很大的枚举types,我想把它转换成一个NSDictionary查找。 我结束了从OSXterminal使用sed

 $ sed -E 's/^[[:space:]]{1,}([[:alnum:]]{1,}).*$/ @(\1) : @"\1",/g' ObservationType.h 

这可以理解为:'捕获第一个单词并输出@(单词):@“单词”,“

这个正则expression式在名为“ObservationType.h”的头文件中转换枚举,其中包含:

 typedef enum : int { ObservationTypePulse = 1, ObservationTypeRespRate = 2, ObservationTypeTemperature = 3, . . } 

变成类似于:

  @(ObservationTypePulse) : @"ObservationTypePulse", @(ObservationTypeRespRate) : @"ObservationTypeRespRate", @(ObservationTypeTemperature) : @"ObservationTypeTemperature", . . 

然后可以使用现代Objective-C语法@{ } (如上面的@ yar1vn所述)将其包装在一个方法中,以创build一个NSDictionary查找:

 -(NSDictionary *)observationDictionary { static NSDictionary *observationDictionary; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ observationDictionary = [[NSDictionary alloc] initWithDictionary:@{ @(ObservationTypePulse) : @"ObservationTypePulse", @(ObservationTypeRespRate) : @"ObservationTypeRespRate", . . }]; }); return observationDictionary; } 

dispatch_once锅炉板只是为了确保静态variables以线程安全的方式初始化。

注:我发现在OSX奇怪的sed正则expression式 – 当我试图用+匹配“一个或多个”它不工作,不得不诉诸使用{1,}作为替代

我在Barry Walk的回答中使用了一个变体,那就是重要性:

  1. 允许编译器检查缺less的case子句(如果你有一个default子句,它不能)。
  2. 使用Objective-C的典型名称(而不是像名字一样的Java)。
  3. 引发特定的例外。
  4. 更短。

例如:

 - (NSString*)describeFormatType:(FormatType)formatType { switch(formatType) { case JSON: return @"JSON"; case XML: return @"XML"; case Atom: return @"Atom"; case RSS: return @"RSS"; } [NSException raise:NSInvalidArgumentException format:@"The given format type number, %ld, is not known.", formatType]; return nil; // Keep the compiler happy - does not understand above line never returns! } 

首先,关于FormatType.JSON:JSON不是FormatType的成员,它是一个可能的types值。 FormatType甚至不是一个复合types – 它是一个标量。

其次,唯一的方法是创build一个映射表。 在Objective-C中执行此操作的更常用的方法是创build一系列引用“符号”的常量,因此您将拥有NSString *FormatTypeJSON = @"JSON"等等。

以下提供了一个解决scheme,以便添加一个新的枚举只需要一个单行编辑,类似的工作,添加一个枚举{}列表中的一行。

 //------------------------------------------------------------------------------ // enum to string example #define FOR_EACH_GENDER(tbd) \ tbd(GENDER_MALE) \ tbd(GENDER_FEMALE) \ tbd(GENDER_INTERSEX) \ #define ONE_GENDER_ENUM(name) name, enum { FOR_EACH_GENDER(ONE_GENDER_ENUM) MAX_GENDER }; #define ONE_GENDER(name) #name, static const char *enumGENDER_TO_STRING[] = { FOR_EACH_GENDER(ONE_GENDER) }; // access string name with enumGENDER_TO_STRING[value] // or, to be safe converting from a untrustworthy caller static const char *enumGenderToString(unsigned int value) { if (value < MAX_GENDER) { return enumGENDER_TO_STRING[value]; } return NULL; } static void printAllGenders(void) { for (int ii = 0; ii < MAX_GENDER; ii++) { printf("%d) gender %s\n", ii, enumGENDER_TO_STRING[ii]); } } //------------------------------------------------------------------------------ // you can assign an arbitrary value and/or information to each enum, #define FOR_EACH_PERSON(tbd) \ tbd(2, PERSON_FRED, "Fred", "Weasley", GENDER_MALE, 12) \ tbd(4, PERSON_GEORGE, "George", "Weasley", GENDER_MALE, 12) \ tbd(6, PERSON_HARRY, "Harry", "Potter", GENDER_MALE, 10) \ tbd(8, PERSON_HERMIONE, "Hermione", "Granger", GENDER_FEMALE, 10) \ #define ONE_PERSON_ENUM(value, ename, first, last, gender, age) ename = value, enum { FOR_EACH_PERSON(ONE_PERSON_ENUM) }; typedef struct PersonInfoRec { int value; const char *ename; const char *first; const char *last; int gender; int age; } PersonInfo; #define ONE_PERSON_INFO(value, ename, first, last, gender, age) \ { ename, #ename, first, last, gender, age }, static const PersonInfo personInfo[] = { FOR_EACH_PERSON(ONE_PERSON_INFO) { 0, NULL, NULL, NULL, 0, 0 } }; // note: if the enum values are not sequential, you need another way to lookup // the information besides personInfo[ENUM_NAME] static void printAllPersons(void) { for (int ii = 0; ; ii++) { const PersonInfo *pPI = &personInfo[ii]; if (!pPI->ename) { break; } printf("%d) enum %-15s %8s %-8s %13s %2d\n", pPI->value, pPI->ename, pPI->first, pPI->last, enumGenderToString(pPI->gender), pPI->age); } } 

@pixel增加了最辉煌的答案在这里: https ://stackoverflow.com/a/24255387/1364257请upvote他!

他使用了20世纪60年代的整洁的Xmacros。 (我已经为现代的ObjC改了他的代码)

 #define X(a, b, c) ab, enum ZZObjectType { XXOBJECTTYPE_TABLE }; typedef NSUInteger TPObjectType; #undef X #define XXOBJECTTYPE_TABLE \ X(ZZObjectTypeZero, = 0, @"ZZObjectTypeZero") \ X(ZZObjectTypeOne, , @"ZZObjectTypeOne") \ X(ZZObjectTypeTwo, , @"ZZObjectTypeTwo") \ X(ZZObjectTypeThree, , @"ZZObjectTypeThree") + (NSString*)nameForObjectType:(ZZObjectType)objectType { #define X(a, b, c) @(a):c, NSDictionary *dict = @{XXOBJECTTYPE_TABLE}; #undef X return dict[objectType]; } 

而已。 干净整洁。 感谢@pixel! https://stackoverflow.com/users/21804/pixel

另一个scheme

 typedef enum BollettinoMavRavTypes { AMZCartServiceOperationCreate, AMZCartServiceOperationAdd, AMZCartServiceOperationGet, AMZCartServiceOperationModify } AMZCartServiceOperation; #define AMZCartServiceOperationValue(operation) [[[NSArray alloc] initWithObjects: @"CartCreate", @"CartAdd", @"CartGet", @"CartModify", nil] objectAtIndex: operation]; 

在你的方法中,你可以使用:

 NSString *operationCheck = AMZCartServiceOperationValue(operation); 

我在这里结合了几种方法。 我喜欢预处理器和索引列表的想法。

没有额外的dynamic分配,并且由于内联,编译器可能能够优化查找。

 typedef NS_ENUM(NSUInteger, FormatType) { FormatTypeJSON = 0, FormatTypeXML, FormatTypeAtom, FormatTypeRSS, FormatTypeCount }; NS_INLINE NSString *FormatTypeToString(FormatType t) { if (t >= FormatTypeCount) return nil; #define FormatTypeMapping(value) [value] = @#value NSString *table[FormatTypeCount] = {FormatTypeMapping(FormatTypeJSON), FormatTypeMapping(FormatTypeXML), FormatTypeMapping(FormatTypeAtom), FormatTypeMapping(FormatTypeRSS)}; #undef FormatTypeMapping return table[t]; } 

许多答案都很好。

如果你在使用一些macros的通用的Objective C解决scheme之后…

关键特性是它使用枚举作为NSString常量的静态数组的索引。 数组本身被封装成一个函数,使其更像苹果API中stream行的NSStringFromXXX函数。

你将需要#import "NSStringFromEnum.h"在这里findhttp://pastebin.com/u83RR3Vk

[编辑]也需要#import "SW+Variadic.h"在这里findhttp://pastebin.com/UEqTzYLf

例1:用string转换器完全定义一个NEW enum typedef。

在myfile.h中


  #import "NSStringFromEnum.h" #define define_Dispatch_chain_cmd(enum)\ enum(chain_done,=0)\ enum(chain_entry)\ enum(chain_bg)\ enum(chain_mt)\ enum(chain_alt)\ enum(chain_for_c)\ enum(chain_while)\ enum(chain_continue_for)\ enum(chain_continue_while)\ enum(chain_break_for)\ enum(chain_break_while)\ enum(chain_previous)\ enum(chain_if)\ enum(chain_else)\ interface_NSString_Enum_DefinitionAndConverters(Dispatch_chain_cmd) 

在myfile.m中:


  #import "myfile.h" implementation_NSString_Enum_Converters(Dispatch_chain_cmd) 

使用:

 NSString *NSStringFromEnumDispatch_chain_cmd(enum Dispatch_chain_cmd value); 

NSStringFromEnumDispatch_chain_cmd(chain_for_c)返回@"chain_for_c"

  enum Dispatch_chain_cmd enumDispatch_chain_cmdFromNSString(NSString *value); 

enumDispatch_chain_cmdFromNSString(@"chain_previous")返回chain_previous

示例2:为现有的枚举提供转换例程,也演示如何使用设置string,以及重命名函数中使用的types名称。

在myfile.h中


  #import "NSStringFromEnum.h" #define CAEdgeAntialiasingMask_SETTINGS_PARAMS CAEdgeAntialiasingMask,mask,EdgeMask,edgeMask interface_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS) 

在myfile.m中:


  // we can put this in the .m file as we are not defining a typedef, just the strings. #define define_CAEdgeAntialiasingMask(enum)\ enum(kCALayerLeftEdge)\ enum(kCALayerRightEdge)\ enum(kCALayerBottomEdge)\ enum(kCALayerTopEdge) implementation_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS) 

这里工作 – > https://github.com/ndpiparava/ObjcEnumString

 //1st Approach #define enumString(arg) (@""#arg) //2nd Approach +(NSString *)secondApproach_convertEnumToString:(StudentProgressReport)status { char *str = calloc(sizeof(kgood)+1, sizeof(char)); int goodsASInteger = NSSwapInt((unsigned int)kgood); memcpy(str, (const void*)&goodsASInteger, sizeof(goodsASInteger)); NSLog(@"%s", str); NSString *enumString = [NSString stringWithUTF8String:str]; free(str); return enumString; } //Third Approcah to enum to string NSString *const kNitin = @"Nitin"; NSString *const kSara = @"Sara"; typedef NS_ENUM(NSUInteger, Name) { NameNitin, NameSara, }; + (NSString *)thirdApproach_convertEnumToString :(Name)weekday { __strong NSString **pointer = (NSString **)&kNitin; pointer +=weekday; return *pointer; } 

给定一个枚举的定义如下:

 typedef NS_ENUM(NSInteger, AssetIdentifier) { Isabella, William, Olivia }; 

我们可以定义一个macros来将枚举值转换为相应的string,如下所示。

 #define AssetIdentifier(asset) \ ^(AssetIdentifier identifier) { \ switch (identifier) { \ case asset: \ default: \ return @#asset; \ } \ }(asset) 

块中使用的switch语句是用于types检查的,也是在Xcode中获得自动完成的支持。

在这里输入图像描述 在这里输入图像描述

根据您的需要,您也可以使用编译器指令来模拟您正在查找的行为。

  #define JSON @"JSON" #define XML @"XML" #define Atom @"Atom" #define RSS @"RSS" 

只记得通常的编译器缺点,(不是types安全的,直接复制粘贴使源文件变大)