Преобразование objective-c typedef в его строковый эквивалент

143

Предполагая, что у меня есть typedef, объявленный в моем .h файле как таковой:

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

Я хотел бы создать функцию, которая преобразует числовое значение typedef в строку. Например, если сообщение [self toString:JSON]было отправлено; он вернет JSON.

Функция будет выглядеть примерно так:

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

Кстати, если я попробую этот синтаксис

[self toString:FormatType.JSON];

чтобы передать значение typedef методу, я получаю сообщение об ошибке. Что мне не хватает?

Craig
источник
См. Мой ответ на stackoverflow.com/questions/6331762/enum-values-to-nsstring-ios для более чистого решения проблемы.
BooTooMany
4
Может быть, нам стоит поддержать язык Swift в enum.
Итачи,
@craig: вот решение github.com/ndpiparava/ObjcEnumString
Nitin
Чтобы найти решение без простого использования специального получателя для перечисления, которое преобразуется в строку, ознакомьтесь с массивом макросов с преобразованием строки: stackoverflow.com/a/53755377/2057171
Альберт Реншоу

Ответы:

135

Это действительно вопрос C, а не специфический для Objective-C (который является надмножеством языка C). Перечисления в C представлены целыми числами. Итак, вам нужно написать функцию, которая возвращает строку с заданным значением перечисления. Есть много способов сделать это. Массив строк, такой, что значение перечисления может использоваться в качестве индекса в массиве или структуре карты (например, NSDictionary), которая сопоставляет значение перечисления с работой строки, но я считаю, что эти подходы не так ясны, как функция, которая делает преобразование явным (и подход с использованием массива, хотя классический Cспособ опасен, если значения вашего перечисления не совпадают с 0). Что-то вроде этого будет работать:

- (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- это тип, а значения перечисления (например JSON, XMLи т. д.) - это значения, которые вы можете присвоить этому типу.

Барри Уорк
источник
127

Вы не можете сделать это легко. В 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]).

Адам Розенфилд
источник
37
вы можете инициализировать массивы с явными индексами, например, string[] = { [XML] = "XML" }чтобы убедиться, что строка правильно соответствует перечислениям
Кристоф
@Christoph: Да, это функция C99, называемая назначенными инициализаторами . Это нормально для использования в Objective-C (который основан на C99), но для общего кода C89 вы не можете их использовать.
Адам Розенфилд
Есть ли другой путь? Например, вернуть перечисление с учетом строки?
Jameo
1
@Jameo: Да, но это не так просто, как поиск в массиве. Вам нужно будет либо выполнить итерацию по FormatType_toString[]массиву и вызвать -isEqualToString:каждый элемент, чтобы найти совпадение, либо использовать тип данных сопоставления, например, NSDictionaryдля поддержки карты обратного поиска.
Адам Розенфилд
1
Уловка Max O хороша в том, чтобы забыть добавить записи в FormatType_toStringмассив.
AechoLiu
50

Мое решение:

изменить: в конце я добавил еще лучшее решение, используя Modern Obj-C

1.
Поместите имена как ключи в массив.
Убедитесь, что индексы являются соответствующими перечислениями и в правильном порядке (в противном случае исключение).
примечание: имена - это свойство, синтезируемое как * _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]);


Ярив Нисим
источник
12
Помните, что каждый раз, когда вы звоните +[typeDisplayNames], вы заново создаете словарь. Это нормально, если он вызывается только несколько раз, но если он вызывается много раз, это будет очень дорого. Лучшее решение может заключаться в том, чтобы сделать словарь одноэлементным, чтобы он создавался только один раз и в противном случае оставался в памяти. Классическая память против головоломки с процессором.
Джоэл Фишер
Или измените ее на статическую переменную, например, static NSDictionary *dict = nil; if(!dict) dict = @{@(UserTypeParent): @"Parent"}; return dict;комментарии не позволят вам разорвать строку, извините за это.
natanavra
29

Комбинируя ответ @AdamRosenfield, комментарий @Christoph и еще один трюк для обработки простых перечислений C, я предлагаю:

// 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]);

В худшем случае - например, если вы измените перечисление, но забудете изменить массив имен - для этого ключа он вернет nil.

Макс O
источник
12

определить перечисление 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 :

/* IngredientType_text */
"IngredientType_0" = "Text";
/* IngredientType_audio */
"IngredientType_1" = "Audio";
/* IngredientType_video */
"IngredientType_2" = "Video";
/* IngredientType_image */
"IngredientType_3" = "Image";
Manitu
источник
11

Я бы использовал строковый токен компилятора # (вместе с макросами, чтобы сделать его более компактным):

#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
}
Пит
источник
Это отлично работало в C99 - я новичок в C, и я нашел, что это самый чистый способ выполнить заданный вопрос. Я также добавил в свою реализацию значение по умолчанию для элементов, которые, возможно, не были определены. Очень чистый метод. Спасибо за результат. Очень хитрое использование макроса.
TravisWhidden
8

Мне нравится, как это #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];
}

источник (источник больше не доступен)

лисица
источник
@ Дайдж-Джан, а как насчет возвращения, nilесли array.count <= enumValue?
anneblue
@anneblue, который поймает ошибку ... он будет хрупким, потому что если вы добавите значение перечисления ИЛИ изменится целочисленное значение значения перечисления, это пойдет не так. Принятый ответ был бы хорош
Дайдж-Джан
@codercat :( извините - не уверен, что случилось с тем сайтом. Не в
обратном направлении,
У меня есть небольшой вопрос по приведенному выше ответу. Как преобразовать строковый элемент в kImageType. Мне нужно вызвать метод imageTypeEnumToString, передав строку. Не могли бы вы помочь мне с моей проблемой.
Ганеш
1
Мне больше всего нравится этот ответ, потому что у вас есть определения строк рядом с перечислениями. Наименьший шанс пропустить значение. А @Ganesh, чтобы преобразовать исходное значение, мог бы сделать это: return (kImageType) [imageTypeArray indexOfObject: rawValue];
Harris
8

Я сделал своего рода смесь всех решений, найденных на этой странице, чтобы создать свое, это своего рода объектно-ориентированное расширение enum или что-то в этом роде.

Фактически, если вам нужно больше, чем просто константы (например, целые числа), вам, вероятно, понадобится объект модели (мы все говорим о MVC, верно?)

Просто задайте себе вопрос, прежде чем использовать это, я прав, вам действительно не нужен реальный объект модели, инициализированный из веб-службы, plist, базы данных SQLite или CoreData?

В любом случае, вот код (MPI для "My Project Initials", кажется, все используют это или свое имя):

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
Dulgan
источник
выглядит красиво, но вы распределяете и возвращаете полные словари, когда вам нужно только одно из его значений. Эффективность VS Красивый код? зависит от того, что вы хотите, и вас это устроит, если вы не будете так часто использовать их в своем коде, как в огромном цикле. Но это может быть полезно с «динамическими» или не жестко закодированными перечислениями, поступающими, например, с сервера
user2387149
5

Другое решение:

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);
Kennymuse
источник
4

Улучшен ответ @ 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)];
}

Таким образом, когда вы измените имя записи перечисления, соответствующая строка будет изменена. Полезно, если вы не собираетесь показывать эту строку пользователю.

Богдан Орлов
источник
Можете ли вы объяснить "- определить VariableName (arg) (@" "# arg) --- и, вероятно, дать лучшее решение?
xySVerma
С #defines, когда вы используете # для замены, аргумент автоматически заключен в двойные кавычки. В C, когда две строки появляются рядом друг с другом в коде, например "foo""bar", "foobar"при компиляции получается строка . Таким образом, #define VariableName(arg) (@""#arg)будет расширяться , VariableName(MyEnum)чтобы быть (@"""MyEnum"). Это приведет к строке @"MyEnum".
Крис Дуглас
3

Учитывая определение перечисления, например:

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

Мы можем определить макрос для преобразования значения перечисления в соответствующую строку, как показано ниже.

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

switchЗаявление используется в блоке для проверки типов, а также получить автозаполнения поддержки в Xcode.

введите описание изображения здесь введите описание изображения здесь

ylin0x81
источник
2

У меня был большой перечислимый тип, который я хотел преобразовать в NSDictionaryпоиск. В итоге я использовал sedтерминал OSX как:

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

который можно читать как: 'захватить первое слово в строке и вывести @ (слово): @ "слово",'

Это регулярное выражение преобразует перечисление в файл заголовка с именем ObservationType.h, который содержит:

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

во что-то вроде:

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

который затем можно обернуть в метод, использующий современный синтаксис objective-c @{ }(как объяснено в @ yar1vn выше) для создания 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Шаблонный только для того , чтобы переменная статического инициализируется в поточно-образом.

Примечание: я обнаружил, что выражение регулярного выражения sed в OSX нечетное - когда я пытался использовать +для сопоставления «один или несколько», оно не сработало, и пришлось прибегнуть к использованию {1,}в качестве замены

Ник Эйджер
источник
2

Я использую вариант ответа Барри Уолка, который в порядке важности:

  1. Позволяет компилятору проверять отсутствие предложений case (это невозможно, если у вас есть предложение по умолчанию).
  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!
}
Говард Ловатт
источник
2

@pixel добавил сюда самый блестящий ответ: https://stackoverflow.com/a/24255387/1364257 Пожалуйста, проголосуйте за него!

Он использует аккуратный макрос X из 1960-х годов. (Я немного изменил его код для современного ObjC)

#define X(a, b, c) a b,
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

Voiger
источник
@AlexandreG предоставь свое решение, чувак. К кому-то легко придираться. У этого решения есть как очевидные плюсы, так и очевидные минусы. Сделайте мир лучше с помощью вашего решения.
voiger
2

Здесь я объединил несколько подходов. Мне нравится идея препроцессора и индексированного списка.

Дополнительного динамического выделения нет, и из-за встраивания компилятор может оптимизировать поиск.

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];
}

источник
1

Прежде всего, что касается FormatType.JSON: JSON не является членом FormatType, это возможное значение типа. FormatType даже не составной тип - это скаляр.

Во-вторых, единственный способ сделать это - создать таблицу сопоставления. Более распространенный способ сделать это в Objective-C - создать серию констант, относящихся к вашим «символам», NSString *FormatTypeJSON = @"JSON"и так далее.

Чак
источник
1

нижеследующее предоставляет решение, в котором для добавления нового перечисления требуется только однострочное редактирование, аналогично добавлению одной строки в список перечислений {}.

//------------------------------------------------------------------------------
// 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);
    }
}
фредворк
источник
Эта техника называется X-Macro, на случай, если кто-то захочет про нее прочитать. Это происходит из-за того, что традиционно макрос FOR_EACH_GENDER () всегда просто вызывался X (). Возможно, вы захотите сделать #undef FOR_EACH_GENDER, прежде чем придать ему новое значение.
uliwitness
1

Каждый ответ здесь в основном говорит об одном и том же: создайте обычное перечисление, а затем используйте настраиваемый геттер для переключения между строками.

Я использую гораздо более простое решение, которое быстрее, короче и чище - с использованием макросов!


#define kNames_allNames ((NSArray <NSString *> *)@[@"Alice", @"Bob", @"Eve"])
#define kNames_alice ((NSString *)kNames_allNames[0])
#define kNames_bob ((NSString *)kNames_allNames[1])
#define kNames_eve ((NSString *)kNames_allNames[2])

Затем вы можете просто начать вводить kNam...текст, и автозаполнение отобразит нужные вам списки!

Кроме того, если вы хотите обрабатывать логику для всех имен сразу, вы можете просто быстро перечислить литеральный массив в следующем порядке:

for (NSString *kName in kNames_allNames) {}

Наконец, приведение NSString в макросах обеспечивает поведение, аналогичное typedef!


Наслаждайтесь!

Альберт Реншоу
источник
0

Многие ответы довольно хороши.

Если вам нужно общее решение Objective C, которое использует некоторые макросы ...

Ключевой особенностью является использование перечисления в качестве индекса в статическом массиве констант NSString. сам массив обернут в функцию, чтобы сделать его более похожим на набор функций NSStringFromXXX, распространенных в API Apple.

вам нужно будет #import "NSStringFromEnum.h"найти здесь http://pastebin.com/u83RR3Vk

[EDIT] также необходимо #import "SW+Variadic.h"найти здесь http://pastebin.com/UEqTzYLf

Пример 1: полностью определить НОВЫЙ 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: предоставить подпрограммы преобразования для существующего перечисления также демонстрирует использование строки настроек и переименование имени типа, используемого в функциях.

в 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)
несинхронизированный
источник
0

Вот работает -> 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;
}
Нитин
источник
поскольку повторяющийся ответ не допускается, вот полное решение github.com/ndpiparava/ObjcEnumString
Nitin
-2

В зависимости от ваших потребностей вы также можете использовать директивы компилятора для имитации поведения, которое вы ищете.

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

Просто помните обычные недостатки компилятора (небезопасный тип, прямое копирование и вставка увеличивает размер исходного файла)

Алекс Госселин
источник
8
Я не думаю, что это сработает; везде, где #defineвиден, вы не сможете использовать фактическое значение перечисления (т.е. JSONбудет заменено @"JSON"препроцессором, что приведет к ошибке компилятора при присвоении a FormatType.
Барри Уорк,