Перетворіть object-c typedef в його еквівалент рядка


141

Якщо припустити, що в моєму файлі .h я вказаний typedef:

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 методу, я отримую помилку. Що я пропускаю?


Дивіться мою відповідь на сайті stackoverflow.com/questions/6331762/enum-values-to-nsstring-ios для більш чіткого вирішення проблеми.
BooTooMany

4
Можливо, ми мусимо обійняти мову Свіфта на перегляді.
Ітачі

@craig: ось рішення github.com/ndpiparava/ObjcEnumString
Нітін

Щоб вирішити, не використовуючи спеціальний getter для перерахунку, який перетворює на рядок, перегляньте макросистему, що передається
Albert Renshaw

Відповіді:


135

Це справді питання C, не характерне для Objective-C (що є набором мови C). Перерахунки в C представлені у вигляді цілих чисел. Тому потрібно написати функцію, яка повертає рядок із заданим значенням enum. Існує багато способів зробити це. Масив рядків, такий, що значення enum може використовуватися в якості індексу в масив або структуру карти (наприклад, an NSDictionary), яка відображає значення enum в рядковій роботі, але я вважаю, що ці підходи не такі чіткі, як функція, яка робить перетворення явним (і підхід до масиву, хоча класичний 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- це тип, а значення enum (наприклад 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 ви не можете їх використовувати.
Адам Розенфілд

Чи є інший шлях? Наприклад, повернути enum назад рядком?
Джамео

1
@Jameo: Так, але це не так просто, як пошук масиву. Вам потрібно буде або перебрати FormatType_toString[]масив і зателефонувати -isEqualToString:на кожен елемент, щоб знайти відповідність, або використовувати тип даних картографування, такий як NSDictionaryпідтримка карти зворотного пошуку.
Адам Розенфілд

1
Трюк Max O хороший у тому, що забули додати записи до FormatType_toStringмасиву.
AechoLiu

50

Моє рішення:

редагувати: Я додав ще краще рішення в кінці, використовуючи Modern Obj-C

1.
Поставте імена як ключі в масиві.
Переконайтеся, що в індексах є відповідні перерахунки, і в правильному порядку (інакше виняток).
Примітка: імена - це властивість, синтезована як * _name *;

код не перевірявся на компіляцію, але я використовував ту саму техніку у своєму додатку.

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, ми можемо використовувати словник для прив’язки описів до клавіш в enum.
Замовлення НЕ має значення .

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;коментарі не дозволять вам перервати рядок, вибачте за це.
натанавра

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

У гіршому випадку - наприклад, якщо ви зміните перерахунок, але забудете змінити масив імен - він поверне нуль для цього ключа.


12

визначити typedef enum у заголовку класу:

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";

11

Я використовував би # string маркера компілятора (разом з макросами, щоб зробити його більш компактним):

#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, і я виявив, що це найчистіший спосіб виконати задане питання. Я також додав за замовчуванням у своїй реалізації для елементів, які, можливо, не були визначені. Дуже чистий метод. Дякуємо за результати Дуже підступне використання макроса.
ТревісВідкритий

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

джерело (джерело більше не доступне)


@ Daij-Djan, що з поверненням, nilякщо array.count <= enumValue?
anneblue

@anneblue, що вловить помилку .. це було б крихким, тому що якщо ви додасте значення перерахунку АБО ціле число змін значення перерахування, це піде не так. Прийнята відповідь була б хорошою
Дайдж-Джан

@codercat :( вибачте - не впевнений, що сталося з цим веб-сайтом. Не в дорозі назад, коли машина також ...
Lindon Fox

У мене є невелике запитання щодо вищезгаданої відповіді. Як перетворити елемент рядка в kImageType. Мені потрібно зателефонувати метод imageTypeEnumToString, передавши рядок. Ви можете, будь ласка, допомогти мені вирішити мою проблему.
Ганеш

1
Мені найбільше подобається ця відповідь, тому що у вас є рядкові визначення прямо поруч із перерахунками. Найменший шанс пропустити значення. І @Ganesh, щоб перетворити з вихідного значення, міг би це зробити: return (kImageType) [imageTypeArray indexOfObject: rawValue];
Харріс

8

Я створив якусь суміш усіх рішень, знайдених на цій сторінці, щоб створити шахту, це свого роду об'єктно-орієнтоване розширення enum чи щось таке.

Насправді, якщо вам потрібно більше, ніж просто константи (тобто цілі числа), вам, мабуть, потрібен об'єкт моделі (ми всі говоримо про MVC, правда?)

Просто задайте собі питання перед тим, як скористатися цим, я правий, чи вам, власне, не потрібен реальний модельний об'єкт, ініціалізований із веб-сервісу, списку, бази даних 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

виглядає приємно, але ви виділяєте і повертаєте повні словники, коли вам потрібно лише одне з його значень. Ефективність 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);

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

Таким чином, коли ви зміните ім'я запису enum, відповідний рядок буде змінено. Корисно в тому випадку, якщо ви не збираєтеся показувати цей рядок користувачеві.


Чи можете ви пояснити "- визначте 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
};

Ми можемо визначити макрос для перетворення значення enum у відповідний рядок, як показано нижче.

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

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

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


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",
    .
    .

який може бути згорнуто у метод, використовуючи сучасний синтаксис @{ }target -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 regex на OSX непарним - коли я намагався використовувати +для узгодження "один чи кілька", це не спрацювало, і довелося вдатися до використання {1,}в якості заміни


2

Я використовую варіацію відповіді Баррі Уола, що за важливістю:

  1. Дозволяє компілятору перевірити наявність відсутніх речей (не може, якщо у вас є за замовчуванням).
  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!
}

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


@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 {}.

//------------------------------------------------------------------------------
// 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, яке використовує деякі макроси ...

Основна особливість полягає в тому, що він використовує enum як індекс в статичний масив констант 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)

у моєму файлі.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)

у моєму файлі.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;
}

оскільки дублююча
Нітін

-2

Залежно від ваших потреб, ви можете альтернативно використовувати директиви компілятора для імітації поведінки, яку ви шукаєте.

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

Просто пам’ятайте про звичні недоліки компілятора (не вводити безпечну, пряма копія вставляє вихідний файл у збільшений розмір)


8
Я не думаю, що це спрацює; де б #defineце не було видно, ви не зможете використовувати фактичне значення перерахунку (тобто JSONзамінить @"JSON"його препроцесором і призведе до помилки компілятора при призначенні FormatType.
Barry Wark
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.