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

Предполагая, что у меня есть 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 методу, я получаю сообщение об ошибке. Что мне не хватает?


person craig    schedule 07.07.2009    source источник
comment
См. Мой ответ на странице stackoverflow.com/questions/6331762/enum -values-to-nsstring-ios для более чистого решения проблемы.   -  person BooTooMany    schedule 23.10.2014
comment
Может быть, нам стоит поддержать язык Swift в enum.   -  person Itachi    schedule 26.08.2016
comment
@craig: вот решение github.com/ndpiparava/ObjcEnumString   -  person Nitin    schedule 05.03.2017
comment
Для решения без простого использования специального получателя для перечисления, которое преобразуется в строку, ознакомьтесь с массивом макросов с преобразованием строки: stackoverflow.com/a/ 53755377/2057171   -  person Albert Renshaw    schedule 13.12.2018


Ответы (21)


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

person Barry Wark    schedule 07.07.2009

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

person Adam Rosenfield    schedule 07.07.2009
comment
вы можете инициализировать массивы с явными индексами, например string[] = { [XML] = "XML" }, чтобы убедиться, что строка правильно соответствует перечислениям - person Christoph; 08.07.2009
comment
@Christoph: Да, это функция C99, называемая назначенными инициализаторами. Это нормально использовать в Objective-C (который основан на C99), но для общего кода C89 вы не можете их использовать. - person Adam Rosenfield; 03.09.2013
comment
Есть ли способ пойти другим путем? Например, вернуть перечисление в виде строки? - person Jameo; 17.02.2014
comment
@Jameo: Да, но это не так просто, как поиск по массиву. Вам нужно будет либо выполнить итерацию по массиву FormatType_toString[] и вызвать -isEqualToString: для каждого элемента, чтобы найти совпадение, либо использовать тип данных сопоставления, такой как NSDictionary, для поддержки карты обратного просмотра. - person Adam Rosenfield; 17.02.2014
comment
Уловка Max O хороша в том, чтобы забыть добавить записи в массив FormatType_toString. - person AechoLiu; 15.12.2015
comment
switch было бы лучше всего для преодоления опасности обновления массива относительно enum. - person Muhammad Umair; 16.05.2016
comment
typedef enum FormatType: NSString следует изменить на typedef enum FormatType: NSUInteger. Интересно, никто этого не осознавал. Во-вторых, передача ему недопустимого перечисления FormatType_toString [5] приведет к сбою - person hariszaman; 24.02.2017
comment
@hariszaman: Теперь исправлено, спасибо, что уловили. Эта ошибка была введена другим пользователем, который редактировал этот ответ некоторое время назад, и до сих пор никто ее не улавливал. - person Adam Rosenfield; 24.02.2017
comment
@AdamRosenfield добавление проверок каждый раз, когда мы обращаемся к массиву, кажется возможным, чем использование словарей с парой ключ-значение? - person hariszaman; 27.02.2017

Мое решение:

edit: в конце я добавил еще лучшее решение, используя 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]);


person Yariv Nissim    schedule 13.05.2012
comment
Помните, что каждый раз, когда вы вызываете +[typeDisplayNames], вы заново создаете словарь. Это нормально, если он вызывается только несколько раз, но если он вызывается много раз, это будет очень дорого. Лучшее решение может заключаться в том, чтобы сделать словарь одноэлементным, чтобы он создавался только один раз и в противном случае оставался в памяти. Классическая память против головоломки с процессором. - person Joel Fischer; 11.04.2014
comment
Или измените его на статическую переменную, например static NSDictionary *dict = nil; if(!dict) dict = @{@(UserTypeParent): @"Parent"}; return dict; Комментарии не позволяют вам разрыв строки, извините за это. - person natanavra; 29.06.2017

Комбинируя ответ @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.

person Max O    schedule 11.07.2012

определить перечисление 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";
person manitu    schedule 09.03.2012

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

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

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

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

person lindon fox    schedule 02.03.2013
comment
@ Дайдж-Джан, а как насчет того, чтобы вернуть nil, если array.count <= enumValue? - person anneblue; 14.03.2014
comment
@anneblue, который поймает ошибку ... он будет хрупким, потому что если вы добавите значение перечисления ИЛИ изменится целочисленное значение значения перечисления, это пойдет не так. Принятый ответ был бы хорош - person Daij-Djan; 14.03.2014
comment
@codercat :( извините - не знаю, что случилось с этим сайтом. Не в пути назад Когда машина тоже ... - person lindon fox; 20.05.2014
comment
У меня есть небольшой вопрос по приведенному выше ответу. Как преобразовать строковый элемент в kImageType. Мне нужно вызвать метод imageTypeEnumToString, передав строку. Не могли бы вы помочь мне решить мою проблему. - person Ganesh; 06.10.2014
comment
Мне больше всего нравится этот ответ, потому что у вас есть определения строк рядом с перечислениями. Наименьший шанс пропустить значение. И @Ganesh, чтобы преобразовать исходное значение, мог бы сделать это: return (kImageType) [imageTypeArray indexOfObject: rawValue]; - person Elijah; 04.01.2015
comment
Опасно, но он сделал то, что мне нужно, по дешевке. Ловко добавленный в #if отладочные проверки, он никогда не должен увидеть свет в продакшене, и только те, кто будет наказан, будут нести ответственность. - person Hunter-Orionnoir; 29.07.2015

Я сделал своего рода смесь всех решений, найденных на этой странице, чтобы создать свое, это своего рода объектно-ориентированное расширение 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
person dulgan    schedule 03.10.2014
comment
выглядит неплохо, но вы распределяете и возвращаете полные словари, когда вам нужно только одно из его значений. Эффективность VS Красивый код? зависит от того, что вы хотите, и вас это устроит, если вы не будете так часто использовать их в своем коде, как в огромном цикле. Но это может быть полезно с динамическими или не жестко закодированными перечислениями, исходящими, например, с сервера. - person user2387149; 10.07.2015

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

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);
person kennymuse    schedule 10.11.2016

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

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

person Bohdan Orlov    schedule 26.03.2014
comment
Можете ли вы объяснить - определить VariableName (arg) (@ # arg) --- и, вероятно, дать лучшее решение? - person xySVerma; 05.06.2014
comment
С #defines, когда вы используете # для подстановки, аргумент автоматически заключен в двойные кавычки. В C, когда две строки появляются рядом друг с другом в коде типа "foo""bar", при компиляции получается строка "foobar". Итак, #define VariableName(arg) (@""#arg) расширится VariableName(MyEnum) до (@"""MyEnum"). В результате получится строка @"MyEnum". - person Chris Douglass; 28.07.2017

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

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

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

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

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

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

person ylin0x81    schedule 08.04.2017

У меня был большой пронумерованный тип, который я хотел преобразовать в поиск 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,} в качестве замены

person Nick Ager    schedule 17.07.2013

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

  1. Позволяет компилятору проверять отсутствие предложений case (это невозможно, если у вас есть предложение по умолчанию).
  2. Использует типичное имя Objective-C (а не имя, подобное Java).
  3. Вызывает конкретное исключение.
  4. Короче.

EG:

- (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!
}
person Howard Lovatt    schedule 20.03.2014

@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

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

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

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

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];
}
person Community    schedule 22.06.2017

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

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

person Chuck    schedule 07.07.2009

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

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

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

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


#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!


Наслаждаться!

person Albert Renshaw    schedule 13.12.2018

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

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

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

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

[РЕДАКТИРОВАТЬ] также необходимо #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)
person unsynchronized    schedule 21.11.2013

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

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

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

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

person Alex Gosselin    schedule 26.05.2011
comment
Я не думаю, что это сработает; везде, где виден #define, вы не сможете использовать фактическое значение перечисления (т.е. JSON будет заменен препроцессором на @"JSON", что приведет к ошибке компилятора при присвоении FormatType. - person Barry Wark; 12.07.2011