Convierta object-c typedef a su cadena equivalente

141

Suponiendo que tengo un typedef declarado en mi archivo .h como tal:

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

Me gustaría construir una función que convierta el valor numérico de typedef en una cadena. Por ejemplo, si el mensaje [self toString:JSON]fue enviado; devolvería 'JSON'.

La función se vería así:

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

Por cierto, si intento esta sintaxis

[self toString:FormatType.JSON];

para pasar el valor typedef al método, me sale un error. ¿Qué me estoy perdiendo?

craig
fuente
Vea mi respuesta en stackoverflow.com/questions/6331762/enum-values-to-nsstring-ios para obtener una solución más limpia al problema.
BooTooMany
44
Tal vez deberíamos abrazar el lenguaje Swift en la enumeración.
Itachi
@craig: aquí está la solución github.com/ndpiparava/ObjcEnumString
Nitin
Para una solución sin simplemente usar un getter personalizado para enum que se convierte en cadena, consulte la matriz de macros de cadena: stackoverflow.com/a/53755377/2057171
Albert Renshaw

Respuestas:

135

Esta es realmente una pregunta C, no específica de Objective-C (que es un superconjunto del lenguaje C). Las enumeraciones en C se representan como enteros. Por lo tanto, debe escribir una función que devuelva una cadena dada un valor de enumeración. Hay muchas maneras de hacer esto. Una matriz de cadenas de modo que el valor enum se pueda usar como un índice en la matriz o una estructura de mapa (por ejemplo, an NSDictionary) que mapea un valor enum a un trabajo de cadena, pero encuentro que estos enfoques no son tan claros como una función que hace que la conversión sea explícita (y el enfoque de matriz, aunque la Cforma clásica es peligrosa si los valores de su enumeración no son distintos de 0). Algo como esto funcionaría:

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

Su pregunta relacionada sobre la sintaxis correcta para un valor de enumeración es que usa solo el valor (por ejemplo JSON), no la FormatType.JSONsintaxis. FormatTypees un tipo y los valores de enumeración (p JSON. ej . XML, etc.) son valores que puede asignar a ese tipo.

Barry Wark
fuente
127

No puedes hacerlo fácilmente. En C y Objective-C, las enumeraciones son realmente constantes enteras glorificadas. Tendrá que generar una tabla de nombres usted mismo (o con algún abuso de preprocesador). Por ejemplo:

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

El peligro de este enfoque es que si alguna vez cambia la enumeración, debe recordar cambiar la matriz de nombres. Puede resolver este problema con algunos abusos del preprocesador, pero es complicado y feo.

También tenga en cuenta que esto supone que tiene una constante enum válida. Si usted tiene un valor entero de una fuente no confiable, que, además, tenga que hacer una verificación de que su constante es válido, por ejemplo, mediante la inclusión de un valor "pasado max" en su enumeración, o comprobando si es menor que la longitud de la matriz, sizeof(FormatType_toString) / sizeof(FormatType_toString[0]).

Adam Rosenfield
fuente
37
puede inicializar matrices con índices explícitos, por ejemplo, string[] = { [XML] = "XML" }para asegurarse de que la cadena coincida con las enumeraciones correctamente
Christoph
@ Christoph: Sí, esa es una característica C99 llamada inicializadores designados . Está bien usarlo en Objective-C (que se basa en C99), pero para el código genérico C89, no puede usarlos.
Adam Rosenfield
¿Hay alguna manera de ir a otro lado? Por ejemplo, ¿recuperar la enumeración dada una cadena?
Jameo
1
@Jameo: Sí, pero no es tan simple como hacer una búsqueda de matriz. Necesitará iterar a través de la FormatType_toString[]matriz y llamar -isEqualToString:a cada elemento para encontrar una coincidencia, o usar un tipo de datos de mapeo como NSDictionarypara mantener el mapa de búsqueda inversa.
Adam Rosenfield
1
El truco de Max O es olvidarse de agregar entradas en la FormatType_toStringmatriz.
AechoLiu
50

Mi solución:

editar: He agregado una solución aún mejor al final, usando Modern Obj-C

1.
Ponga los nombres como claves en una matriz.
Asegúrese de que los índices son las enumeraciones apropiadas y en el orden correcto (de lo contrario, excepción).
nota: nombres es una propiedad sintetizada como * _nombres *;

no se verificó la compilación del código, pero utilicé la misma técnica en mi aplicación.

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.
Usando Modern Obj-C puedes usar un diccionario para vincular las descripciones a las claves en la enumeración.
El orden NO importa .

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


Uso (en un método de instancia de clase):

NSLog(@"%@", [self typeDisplayName]);


Yariv Nissim
fuente
12
Tenga en cuenta que cada vez que llama +[typeDisplayNames], está recreando el diccionario. Esto está bien si solo se llama unas pocas veces, pero si se llama muchas veces, esto será muy costoso. Una mejor solución puede ser convertir el diccionario en un singleton, por lo que solo se crea una vez y, de lo contrario, permanece en la memoria. Memoria clásica frente al enigma de la CPU.
Joel Fischer
O cámbielo para que sea una variable estática, por ejemplo, los static NSDictionary *dict = nil; if(!dict) dict = @{@(UserTypeParent): @"Parent"}; return dict;comentarios no le permitirán un salto de línea, lo siento.
natanavra
29

Combinando la respuesta de @AdamRosenfield, el comentario de @Christoph y otro truco para manejar enumeraciones en C simples, sugiero:

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

En el peor de los casos, como si cambia la enumeración pero olvida cambiar la matriz de nombres, devolverá nil para esta clave.

Max O
fuente
12

define typedef enum en el encabezado de clase:

typedef enum {
    IngredientType_text  = 0,
    IngredientType_audio = 1,
    IngredientType_video = 2,
    IngredientType_image = 3
} IngredientType;

escribe un método como este en clase:

+ (NSString*)typeStringForType:(IngredientType)_type {
   NSString *key = [NSString stringWithFormat:@"IngredientType_%i", _type];
   return NSLocalizedString(key, nil);
}

tener las cadenas dentro del archivo Localizable.strings :

/* IngredientType_text */
"IngredientType_0" = "Text";
/* IngredientType_audio */
"IngredientType_1" = "Audio";
/* IngredientType_video */
"IngredientType_2" = "Video";
/* IngredientType_image */
"IngredientType_3" = "Image";
kitschmaster
fuente
11

Usaría el token de cadena # del compilador (junto con las macros para hacerlo todo más compacto):

#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
}
Pete
fuente
Esto funcionó muy bien en C99: soy nuevo en C y descubrí que esta es la forma más limpia de lograr la pregunta que se me hizo. También agregué un valor predeterminado en mi implementación para elementos que pueden no haber sido definidos. Método muy limpio. Gracias por los resultados. Uso muy astuto de una macro.
TravisWhidden
8

Me gusta la #defineforma de hacer esto:

// Coloque esto en su archivo .h, fuera del bloque @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];
}

fuente (fuente ya no está disponible)

lindon fox
fuente
@ Daij-Djan, ¿qué hay de volver nilsi array.count <= enumValue?
anneblue
@anneblue que detectaría el error ... aún sería frágil porque si agrega un valor de enumeración O el valor entero de un valor de enumeración cambia, esto sale mal. La respuesta aceptada sería buena
Daij-Djan
@codercat :( lo siento, no estoy seguro de lo que sucedió con ese sitio web. No en el camino de regreso cuando la máquina tampoco ...
lindon fox
Tengo una pequeña pregunta sobre la respuesta anterior. Cómo convertir un elemento de cadena a kImageType. Necesito llamar al método imageTypeEnumToString pasando la cadena. ¿Pueden ayudarme con mi problema?
Ganesh
1
Me gusta más esta respuesta, porque tienes las definiciones de cadena justo al lado de las enumeraciones. Menor posibilidad de perder un valor. Y @Ganesh, para convertir del valor bruto, podría hacer esto: return (kImageType) [imageTypeArray indexOfObject: rawValue];
Harris
8

Hice una especie de combinación de todas las soluciones encontradas en esta página para crear la mía, es una especie de extensión de enumeración orientada a objetos o algo así.

De hecho, si necesita algo más que constantes (es decir, enteros), probablemente necesite un objeto modelo (Todos estamos hablando de MVC, ¿verdad?)

Simplemente hágase la pregunta antes de usar esto, ¿estoy en lo cierto, de hecho, no necesita un objeto modelo real, inicializado desde un servicio web, un plist, una base de datos SQLite o CoreData?

De todos modos, aquí viene el código (MPI es para "My Project Initials", todos usan esto o su nombre, parece):

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

Y 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
fuente
se ve bien, pero está asignando y devolviendo diccionarios completos cuando solo necesita uno de sus valores. Eficiencia VS código bonito? depende de lo que desee y estará bien con esto si no los usa tanto en su código como en un gran bucle. Pero esto puede ser útil con enumeraciones "dinámicas" o no codificadas que provienen de un servidor, por ejemplo
user2387149
5

Otra solución:

typedef enum BollettinoMavRavTypes {
    AMZCartServiceOperationCreate,
    AMZCartServiceOperationAdd,
    AMZCartServiceOperationGet,
    AMZCartServiceOperationModify
} AMZCartServiceOperation;

#define AMZCartServiceOperationValue(operation) [[[NSArray alloc] initWithObjects: @"CartCreate", @"CartAdd", @"CartGet", @"CartModify", nil] objectAtIndex: operation];

En su método puede usar:

NSString *operationCheck = AMZCartServiceOperationValue(operation);
kennymuse
fuente
4

Respuesta mejorada @ yar1vn al eliminar la dependencia de la cadena:

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

Por lo tanto, cuando cambie el nombre de la entrada enum, se cambiará la cadena correspondiente. Útil en caso de que no vaya a mostrar esta cadena al usuario.

Bohdan Orlov
fuente
¿Puedes explicar "- define VariableName (arg) (@" "# arg) --- y probablemente dar una mejor solución?
xySVerma
Con #defines, cuando usa # para una sustitución, el argumento se ajusta automáticamente entre comillas dobles. En C, cuando aparecen dos cadenas una al lado de la otra en un código similar "foo""bar", se genera la cadena "foobar"cuando se compila. Entonces, #define VariableName(arg) (@""#arg)se expandirá VariableName(MyEnum)para ser (@"""MyEnum"). Eso dará como resultado la cadena @"MyEnum".
Chris Douglass
3

Dada una definición de enumeración como:

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

Podemos definir una macro para convertir un valor de enumeración en su cadena correspondiente, como se muestra a continuación.

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

La switchdeclaración utilizada en el bloque es para la verificación de tipos y también para obtener el soporte de autocompletar en Xcode.

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

ylin0x81
fuente
2

Tenía un tipo enumerado grande que quería convertirlo en una NSDictionarybúsqueda. Terminé usando seddesde el terminal OSX como:

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

que se puede leer como: 'captura la primera palabra en la línea y genera @ (palabra): @ "palabra",'

Esta expresión regular convierte la enumeración en un archivo de encabezado llamado 'ObservationType.h' que contiene:

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

en algo como:

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

que luego se puede envolver en un método que usa la sintaxis moderna del objetivo-c @{ }(como se explica en @ yar1vn arriba) para crear una NSDictionarybúsqueda:

-(NSDictionary *)observationDictionary
{
    static NSDictionary *observationDictionary;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        observationDictionary = [[NSDictionary alloc] initWithDictionary:@{
                                 @(ObservationTypePulse) : @"ObservationTypePulse",
                                 @(ObservationTypeRespRate) : @"ObservationTypeRespRate",
                                 .
                                 .
                                 }];
    });
    return observationDictionary;
}

La dispatch_onceplaca de la caldera es solo para garantizar que la variable estática se inicialice de manera segura.

Nota: Encontré que la expresión regex de sed en OSX es extraña; cuando traté de usar +para hacer coincidir 'uno o más', no funcionó y tuve que recurrir a usarla {1,}como reemplazo

Nick Ager
fuente
2

Utilizo una variación en la respuesta de Barry Walk, que en orden de importancia:

  1. Permite al compilador verificar las cláusulas de casos faltantes (no puede hacerlo si tiene una cláusula predeterminada).
  2. Utiliza un nombre típico de Objective-C (en lugar de un nombre similar a Java).
  3. Plantea una excepción específica.
  4. Es más corto

P.EJ:

- (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!
}
Howard Lovatt
fuente
2

@pixel agregó la respuesta más brillante aquí: https://stackoverflow.com/a/24255387/1364257 ¡ Por favor, vótelo!

Utiliza la ordenada macro X de la década de 1960. (He cambiado un poco su código para el ObjC moderno)

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

Eso es. Limpio y aseado. Gracias a @pixel! https://stackoverflow.com/users/21804/pixel

voiger
fuente
@AlexandreG proporciona su solución, hombre. Es fácil carparse con alguien. Esta solución tiene sus ventajas y desventajas obvias. Mejora el mundo con tu solución.
voiger
2

Combiné varios enfoques aquí. Me gusta la idea del preprocesador y la lista indexada.

No hay asignación dinámica adicional, y debido a la alineación, el compilador podría optimizar la búsqueda.

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

fuente
1

En primer lugar, con respecto a FormatType.JSON: JSON no es miembro de FormatType, es un posible valor del tipo. FormatType ni siquiera es un tipo compuesto, es un escalar.

En segundo lugar, la única forma de hacerlo es crear una tabla de mapeo. La forma más común de hacer esto en Objective-C es crear una serie de constantes que se refieran a sus "símbolos", para que así sea, NSString *FormatTypeJSON = @"JSON"y así sucesivamente.

Arrojar
fuente
1

Lo siguiente proporciona una solución tal que agregar una nueva enumeración requiere solo una edición de una línea, un trabajo similar a agregar una sola línea en una lista de enumeración {}.

//------------------------------------------------------------------------------
// 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);
    }
}
fredwork
fuente
Esta técnica se llama X-Macro, en caso de que alguien quiera leer al respecto. Esto viene del hecho de que, tradicionalmente, la macro FOR_EACH_GENDER () siempre se llamaba simplemente X (). Una cosa que puede hacer es #undef FOR_EACH_GENDER antes de redefinirlo con un nuevo significado.
uliwitness
1

Cada respuesta aquí básicamente dice lo mismo, crea una enumeración regular y luego usa un captador personalizado para cambiar entre cadenas.

Utilizo una solución mucho más simple que es más rápida, más corta y más limpia, ¡usando Macros!


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

¡Entonces simplemente puede comenzar a escribir kNam...y autocompletar mostrará las listas que desee!

Además, si desea manejar la lógica de todos los nombres a la vez, simplemente puede enumerar rápidamente la matriz literal en orden, de la siguiente manera:

for (NSString *kName in kNames_allNames) {}

Por último, la conversión de NSString en las macros garantiza un comportamiento similar al de typedef!


¡Disfrutar!

Albert Renshaw
fuente
0

Muchas respuestas, todas bastante buenas.

Si buscas una solución genérica de Objective C que usa algunas macros ...

La característica clave es que utiliza la enumeración como un índice en una matriz estática de constantes NSString. la matriz en sí está envuelta en una función para que se parezca más al conjunto de funciones NSStringFromXXX que prevalecen en las API de Apple.

Necesitará #import "NSStringFromEnum.h"encontrarlo aquí http://pastebin.com/u83RR3Vk

[EDITAR] también necesita #import "SW+Variadic.h"encontrarse aquí http://pastebin.com/UEqTzYLf

Ejemplo 1: defina completamente un NUEVO enum typedef, con convertidores de cadenas.

en mi archivo.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)

en myfile.m:


 #import "myfile.h"

 implementation_NSString_Enum_Converters(Dispatch_chain_cmd)

usar :

NSString *NSStringFromEnumDispatch_chain_cmd(enum Dispatch_chain_cmd value);

NSStringFromEnumDispatch_chain_cmd(chain_for_c) devoluciones @"chain_for_c"

  enum Dispatch_chain_cmd enumDispatch_chain_cmdFromNSString(NSString *value);

enumDispatch_chain_cmdFromNSString(@"chain_previous") devoluciones chain_previous

Ejemplo 2: proporcionar rutinas de conversión para una enumeración existente también demuestra el uso de una cadena de configuración y el cambio de nombre del nombre de tipo utilizado en las funciones.

en mi archivo.h


 #import "NSStringFromEnum.h"


 #define CAEdgeAntialiasingMask_SETTINGS_PARAMS CAEdgeAntialiasingMask,mask,EdgeMask,edgeMask

 interface_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS)

en 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)
no sincronizado
fuente
0

Aquí está trabajando -> 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;
}
Nitina
fuente
dado que no se permite la respuesta duplicada, aquí está la solución completa github.com/ndpiparava/ObjcEnumString
Nitin
-2

Dependiendo de sus necesidades, podría utilizar alternativamente las directivas del compilador para simular el comportamiento que está buscando.

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

Solo recuerde las deficiencias habituales del compilador (no escribir con seguridad, copiar y pegar directamente aumenta el tamaño del archivo fuente)

Alex Gosselin
fuente
8
No creo que esto funcione; en cualquier lugar que #defineesté visible, no podrá usar el valor de enumeración real (es decir, JSONserá reemplazado @"JSON"por el preprocesador y generará un error de compilación al asignar a un FormatType.)
Barry Wark