将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,枚举值(例如JSON
, XML
等)是可以分配给该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的回答中使用了一个变体,那就是重要性:
- 允许编译器检查缺less的case子句(如果你有一个default子句,它不能)。
- 使用Objective-C的典型名称(而不是像名字一样的Java)。
- 引发特定的例外。
- 更短。
例如:
- (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安全的,直接复制粘贴使源文件变大)