перечисление значений в NSString (iOS)
У меня есть перечисление, содержащее несколько значений:
enum {value1, value2, value3} myValue;
в какой-то момент в моем приложении, я хочу проверить, что значение перечисления находится в активном. Я использую NSLog, но я не понимаю, как отобразить текущее значение перечисления (value1/valu2/valu3/etc...) как NSString для NSLog.
кого?
15 ответов:
это ответ здесь:несколько предложений по реализации
суть в
Objective-C
использует обычный, старыйC
enum
, который является просто прославленным набором целых чисел.предоставлена
enum
такой:typedef enum { a, b, c } FirstThreeAlpha;
ваш метод будет выглядеть так:
- (NSString*) convertToString:(FirstThreeAlpha) whichAlpha { NSString *result = nil; switch(whichAlpha) { case a: result = @"a"; break; case b: result = @"b"; break; case c: result = @"c"; break; default: result = @"unknown"; } return result; }
мне не понравилось помещать перечисление в кучу, не предоставляя функцию кучи для перевода. Вот что я придумал:
typedef enum {value1, value2, value3} myValue; #define myValueString(enum) [@[@"value1",@"value2",@"value3"] objectAtIndex:enum]
это держит перечисление и строковые объявления близко друг к другу для легкого обновления, когда это необходимо.
Теперь, в любом месте кода, Вы можете использовать перечисление / макрос следующим образом:
myValue aVal = value2; NSLog(@"The enum value is '%@'.", myValueString(aVal)); outputs: The enum value is 'value2'.
чтобы гарантировать индексы элементов, вы всегда можете явно объявить начальные (или все) значения перечисления.
enum {value1=0, value2=1, value3=2};
я представлю, как я использую, и это выглядит лучше, чем предыдущий ответ.(Я думаю)
Я хотел бы проиллюстрировать с UIImageOrientation для облегчения понимания.
typedef enum { UIImageOrientationUp = 0, // default orientation, set to 0 so that it always starts from 0 UIImageOrientationDown, // 180 deg rotation UIImageOrientationLeft, // 90 deg CCW UIImageOrientationRight, // 90 deg CW UIImageOrientationUpMirrored, // as above but image mirrored along other axis. horizontal flip UIImageOrientationDownMirrored, // horizontal flip UIImageOrientationLeftMirrored, // vertical flip UIImageOrientationRightMirrored, // vertical flip } UIImageOrientation;
создать метод типа:
NSString *stringWithUIImageOrientation(UIImageOrientation input) { NSArray *arr = @[ @"UIImageOrientationUp", // default orientation @"UIImageOrientationDown", // 180 deg rotation @"UIImageOrientationLeft", // 90 deg CCW @"UIImageOrientationRight", // 90 deg CW @"UIImageOrientationUpMirrored", // as above but image mirrored along other axis. horizontal flip @"UIImageOrientationDownMirrored", // horizontal flip @"UIImageOrientationLeftMirrored", // vertical flip @"UIImageOrientationRightMirrored", // vertical flip ]; return (NSString *)[arr objectAtIndex:input]; }
все, что вам нужно сделать, это :
имя вашей функции.
скопируйте содержимое перечисления и вставьте его между NSArray * arr = @[ и ]; return (NSString *) [arr objectAtIndex: input];
поставить некоторые @ , " , и запятая
профит!!!!
это будет проверено компилятором, так что вы не будете перепутать индексы случайно.
NSDictionary *stateStrings = @{ @(MCSessionStateNotConnected) : @"MCSessionStateNotConnected", @(MCSessionStateConnecting) : @"MCSessionStateConnecting", @(MCSessionStateConnected) : @"MCSessionStateConnected", }; NSString *stateString = [stateStrings objectForKey:@(state)];
var stateStrings: [MCSessionState: String] = [ MCSessionState.NotConnected : "MCSessionState.NotConnected", MCSessionState.Connecting : "MCSessionState.Connecting", MCSessionState.Connected : "MCSessionState.Connected" ] var stateString = stateStrings[MCSessionState.Connected]
Я нашел это сайт (из которого взят пример ниже), который обеспечивает элегантное решение этой проблемы. Исходное сообщение, хотя и исходит из этого StackOverflow ответ.
// Place this in your .h file, outside the @interface block 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]; } // A method to retrieve the int value from the NSArray of NSStrings -(kImageType) imageTypeStringToEnum:(NSString*)strVal { NSArray *imageTypeArray = [[NSArray alloc] initWithObjects:kImageTypeArray]; NSUInteger n = [imageTypeArray indexOfObject:strVal]; if(n < 1) n = JPG; return (kImageType) n; }
если я могу предложить другое решение, которое имеет дополнительное преимущество проверки типа, предупреждения, если вам не хватает значения enum в вашем преобразовании, читаемости и краткости.
для примера:
typedef enum { value1, value2, value3 } myValue;
можно сделать так:NSString *NSStringFromMyValue(myValue type) { const char* c_str = 0; #define PROCESS_VAL(p) case(p): c_str = #p; break; switch(type) { PROCESS_VAL(value1); PROCESS_VAL(value2); PROCESS_VAL(value3); } #undef PROCESS_VAL return [NSString stringWithCString:c_str encoding:NSASCIIStringEncoding]; }
в качестве примечания. Это лучший подход, чтобы объявить ваши перечисления так:
typedef NS_ENUM(NSInteger, MyValue) { Value1 = 0, Value2, Value3 }
С этим вы получаете тип-безопасность (
NSInteger
в этом случае), вы устанавливаете ожидаемое смещение перечисления (= 0
).
в некоторых случаях, когда вам нужно преобразовать enum - > NSString и NSString - > enum может быть проще использовать typedef и #define (или const NSStrings) вместо enum:
typedef NSString * ImageType; #define ImageTypeJpg @"JPG" #define ImageTypePng @"PNG" #define ImageTypeGif @"GIF"
а затем просто работать с" именованными " строками, как и с любой другой NSString:
@interface MyData : NSObject @property (copy, nonatomic) ImageType imageType; @end @implementation MyData - (void)doSomething { //... self.imageType = ImageTypePng; //... if ([self.imageType isEqualToString:ImageTypeJpg]) { //... } } @end
В приведенном ниже решении используется оператор stringize препроцессора, что позволяет получить более элегантное решение. Это позволяет определить термины перечисления только в одном месте для большей устойчивости к опечаткам.
во-первых, определите свое перечисление следующим образом.
#define ENUM_TABLE \ X(ENUM_ONE), \ X(ENUM_TWO) \ #define X(a) a typedef enum Foo { ENUM_TABLE } MyFooEnum; #undef X #define X(a) @#a NSString * const enumAsString[] = { ENUM_TABLE }; #undef X
Теперь используйте его следующим образом:
// Usage MyFooEnum t = ENUM_ONE; NSLog(@"Enum test - t is: %@", enumAsString[t]); t = ENUM_TWO; NSLog(@"Enum test - t is now: %@", enumAsString[t]);
выходы:
2014-10-22 13:36:21.344 FooProg[367:60b] Enum test - t is: ENUM_ONE 2014-10-22 13:36:21.344 FooProg[367:60b] Enum test - t is now: ENUM_TWO
ответ@pixel указал мне в правильном направлении.
можно использовать X-макросы - они идеально подходят для этого.
преимущества 1. связь между фактическим значением перечисления и строковым значением находится в одном месте. 2. вы можете использовать обычные инструкции switch позже в своем коде.
ущерб 1. Начальный код установки немного тупой, и использует забавные макросы.
код
#define X(a, b, c) a b, enum ZZObjectType { ZZOBJECTTYPE_TABLE }; typedef NSUInteger TPObjectType; #undef X #define XXOBJECTTYPE_TABLE \ X(ZZObjectTypeZero, = 0, "ZZObjectTypeZero") \ X(ZZObjectTypeOne, = 1, "ZZObjectTypeOne") \ X(ZZObjectTypeTwo, = 2, "ZZObjectTypeTwo") \ X(ZZObjectTypeThree, = 3, "ZZObjectTypeThree") \ + (NSString*)nameForObjectType:(ZZObjectType)objectType { #define X(a, b, c) @c, [NSNumber numberWithInteger:a], NSDictionary *returnValue = [NSDictionary dictionaryWithObjectsAndKeys:ZZOBJECTTYPE_TABLE nil]; #undef X return [returnValue objectForKey:[NSNumber numberWithInteger:objectType]]; } + (ZZObjectType)objectTypeForName:(NSString *)objectTypeString { #define X(a, b, c) [NSNumber numberWithInteger:a], @c, NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:ZZOBJECTSOURCE_TABLE nil]; #undef X NSUInteger value = [(NSNumber *)[dictionary objectForKey:objectTypeString] intValue]; return (ZZObjectType)value; }
теперь вы можете сделать:
NSString *someString = @"ZZObjectTypeTwo" ZZObjectType objectType = [[XXObject objectTypeForName:someString] intValue]; switch (objectType) { case ZZObjectTypeZero: // break; case ZZObjectTypeOne: // break; case ZZObjectTypeTwo: // break; }
этот шаблон был с 1960-х годов (без шуток!): http://en.wikipedia.org/wiki/X_Macro
вот решение plug-and-play, которое вы можете расширить с помощью простого копирования и вставки существующих определений.
Я надеюсь, что вы все найдете его полезным, как я нашел полезным так много других решений StackOverflow.
- (NSString*) enumItemNameForPrefix:(NSString*)enumPrefix item:(int)enumItem { NSString* enumList = nil; if ([enumPrefix isEqualToString:@"[Add Your Enum Name Here"]) { // Instructions: // 1) leave all code as is (it's good reference and won't conflict) // 2) add your own enums below as follows: // 2.1) duplicate the LAST else block below and add as many enums as you like // 2.2) Copy then Paste your list, including carraige returns // 2.3) add a back slash at the end of each line to concatenate the broken string // 3) your are done. } else if ([enumPrefix isEqualToString:@"ExampleNonExplicitType"]) { enumList = @" \ ExampleNonExplicitTypeNEItemName1, \ ExampleNonExplicitTypeNEItemName2, \ ExampleNonExplicitTypeNEItemName3 \ "; } else if ([enumPrefix isEqualToString:@"ExampleExplicitAssignsType"]) { enumList = @" \ ExampleExplicitAssignsTypeEAItemName1 = 1, \ ExampleExplicitAssignsTypeEAItemName2 = 2, \ ExampleExplicitAssignsTypeEAItemName3 = 4 \ "; } else if ([enumPrefix isEqualToString:@"[Duplicate and Add Your Enum Name Here #1"]) { // Instructions: // 1) duplicate this else block and add as many enums as you like // 2) Paste your list, including carraige returns // 3) add a back slash at the end of each line to continue/concatenate the broken string enumList = @" \ [Replace only this line: Paste your Enum Definition List Here] \ "; } // parse it int implicitIndex = 0; NSString* itemKey = nil; NSString* itemValue = nil; NSArray* enumArray = [enumList componentsSeparatedByString:@","]; NSMutableDictionary* enumDict = [[[NSMutableDictionary alloc] initWithCapacity:enumArray.count] autorelease]; for (NSString* itemPair in enumArray) { NSArray* itemPairArray = [itemPair componentsSeparatedByString:@"="]; itemValue = [[itemPairArray objectAtIndex:0] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; itemKey = [NSString stringWithFormat:@"%d", implicitIndex]; if (itemPairArray.count > 1) itemKey = [[itemPairArray lastObject] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; [enumDict setValue:itemValue forKey:itemKey]; implicitIndex++; } // return value with or without prefix NSString* withPrefix = [enumDict valueForKey:[NSString stringWithFormat:@"%d", enumItem]]; NSString* withoutPrefix = [withPrefix stringByReplacingOccurrencesOfString:enumPrefix withString:@""]; NSString* outValue = (0 ? withPrefix : withoutPrefix); if (0) NSLog(@"enum:%@ item:%d retVal:%@ dict:%@", enumPrefix, enumItem, outValue, enumDict); return outValue; }
вот пример объявления:
typedef enum _type1 { ExampleNonExplicitTypeNEItemName1, ExampleNonExplicitTypeNEItemName2, ExampleNonExplicitTypeNEItemName3 } ExampleNonExplicitType; typedef enum _type2 { ExampleExplicitAssignsTypeEAItemName1 = 1, ExampleExplicitAssignsTypeEAItemName2 = 2, ExampleExplicitAssignsTypeEAItemName3 = 4 } ExampleExplicitAssignsType;
вот пример вызова:
NSLog(@"EXAMPLE: type1:%@ type2:%@ ", [self enumItemNameForPrefix:@"ExampleNonExplicitType" item:ExampleNonExplicitTypeNEItemName2], [self enumItemNameForPrefix:@"ExampleExplicitAssignsType" item:ExampleExplicitAssignsTypeEAItemName3]);
наслаждайтесь! ; -)
Ниже приведен пример структуры Enum, которая является Objective-C дружественной в случае, если вам нужно использовать Swift-код в устаревших проектах, написанных на Objective-C.
пример:
contentType.имя файла. toString ()
возвращает "filename"
contentType.имя файла. rawValue
возвращает значение Int, 1 (так как его второй пункт на структуры)
@objc enum contentType:Int { //date when content was created [RFC2183] case creationDate //name to be used when creating file [RFC2183] case filename //whether or not processing is required [RFC3204] case handling //date when content was last modified [RFC2183] case modificationDate //original field name in form [RFC7578] case name //Internet media type (and parameters) of the preview output desired from a processor by the author of the MIME content [RFC-ietf-appsawg-text-markdown-12] case previewType //date when content was last read [RFC2183] case readDate //approximate size of content in octets [RFC2183] case size //type or use of audio content [RFC2421] case voice func toString() -> String { switch self { case .creationDate: return "creation-date" case .filename: return "filename" case .handling: return "handling" case .modificationDate: return "modification-date" case .name: return "name" case .previewType: return "preview-type" case .readDate: return "read-date" case .size: return "size" case .voice: return "voice" } }//eom }//eo-enum
Это старый вопрос, но если у вас есть несмежное перечисление, используйте литерал словаря вместо массива:
typedef enum { value1 = 0, value2 = 1, value3 = 2, // beyond value3 value1000 = 1000, value1001 } MyType; #define NSStringFromMyType( value ) \ ( \ @{ \ @( value1 ) : @"value1", \ @( value2 ) : @"value2", \ @( value3 ) : @"value3", \ @( value1000 ) : @"value1000", \ @( value1001 ) : @"value1001", \ } \ [ @( value ) ] \ )
это похоже на сообщение макроса" X " по пикселям. спасибо за ссылку на http://en.wikipedia.org/wiki/X_Macro
код, созданный в макросах, может быть сложным и трудным для отладки. Вместо этого создайте таблицу, которая используется" обычным " кодом. Я считаю, что многие люди возражают против того, чтобы макросы генерировали код, и может быть одной из причин техника "X макросов", как представлено Вики не получила широкого распространения.
создавая таблицу, вам все равно нужно отредактировать только один место для расширения списка, и поскольку вы не можете "пройти" через таблицу в отладчике, это удаляет возражение от многих людей о многострочном коде, похороненном в макросах.
//------------------------------------------------------------------------------ // 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); } }
макрос:
#define stringWithLiteral(literal) @#literal
перечислимый:
typedef NS_ENUM(NSInteger, EnumType) { EnumType0, EnumType1, EnumType2 };
массив:
static NSString * const EnumTypeNames[] = { stringWithLiteral(EnumType0), stringWithLiteral(EnumType1), stringWithLiteral(EnumType2) };
использование:
EnumType enumType = ...; NSString *enumName = EnumTypeNames[enumType];
= = = = EDIT ====
скопируйте следующий код в свой проект и запустите.
#define stringWithLiteral(literal) @#literal typedef NS_ENUM(NSInteger, EnumType) { EnumType0, EnumType1, EnumType2 }; static NSString * const EnumTypeNames[] = { stringWithLiteral(EnumType0), stringWithLiteral(EnumType1), stringWithLiteral(EnumType2) }; - (void)test { EnumType enumType = EnumType1; NSString *enumName = EnumTypeNames[enumType]; NSLog(@"enumName: %@", enumName); }
вот рабочий код 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; }