Converter objetivo-c typedef em seu equivalente de sequência

141

Supondo que eu tenha um typedef declarado no meu arquivo .h como tal:

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

Gostaria de criar uma função que converte o valor numérico do typedef em uma string. Por exemplo, se a mensagem [self toString:JSON]foi enviada; retornaria 'JSON'.

A função ficaria assim:

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

Aliás, se eu tentar esta sintaxe

[self toString:FormatType.JSON];

para passar o valor typedef para o método, recebo um erro. o que estou perdendo?

craig
fonte
Veja minha resposta em stackoverflow.com/questions/6331762/enum-values-to-nsstring-ios para obter uma solução mais limpa para o problema.
BooTooMany
4
Talvez devêssemos dar um abraço na linguagem Swift no enum.
Itachi
@craig: aqui está a solução github.com/ndpiparava/ObjcEnumString
Nitin
Para uma solução sem simplesmente usando um getter personalizado para enum que se converte em seqüência, consulte a série macro cadeia de elenco: stackoverflow.com/a/53755377/2057171
Albert Renshaw

Respostas:

135

Esta é realmente uma pergunta em C, não específica ao Objective-C (que é um superconjunto da linguagem C). Enums em C são representados como números inteiros. Portanto, você precisa escrever uma função que retorne uma string com um valor enum. Há muitas maneiras de fazer isso. Uma matriz de seqüências de caracteres, de modo que o valor de enum possa ser usado como um índice para a matriz ou uma estrutura de mapa (por exemplo, uma NSDictionary) que mapeia um valor de enum para um trabalho de string, mas acho que essas abordagens não são tão claras quanto uma função que torna a conversão explícita (e a abordagem de matriz, embora a Cmaneira clássica seja perigosa se seus valores de enumeração não forem contíguos de 0). Algo assim funcionaria:

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

Sua pergunta relacionada sobre a sintaxe correta para um valor de enum é que você usa apenas o valor (por exemplo JSON), não a FormatType.JSONsintaxe. FormatTypeé um tipo e os valores enum (por exemplo JSON, XML, etc.) são os valores que você pode atribuir a esse tipo.

Barry Wark
fonte
127

Você não pode fazer isso facilmente. Em C e Objective-C, enums são realmente apenas constantes inteiras glorificadas. Você terá que gerar uma tabela de nomes por conta própria (ou com algum abuso do pré-processador). Por exemplo:

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

O perigo dessa abordagem é que, se você alterar o enum, lembre-se de alterar a matriz de nomes. Você pode resolver esse problema com algum abuso do pré-processador, mas é complicado e feio.

Observe também que isso pressupõe que você tenha uma constante de enumeração válida. Se você possui um valor inteiro de uma fonte não confiável, você também precisa verificar se sua constante é válida, por exemplo, incluindo um valor "passado máximo" em sua enumeração ou verificando se é menor que o comprimento da matriz sizeof(FormatType_toString) / sizeof(FormatType_toString[0]).

Adam Rosenfield
fonte
37
você pode inicializar matrizes com índices explícitas, por exemplo, string[] = { [XML] = "XML" }para garantir que a corda igualar os enums corretamente
Christoph
@Christoph: Sim, esse é um recurso do C99 chamado inicializadores designados . Não há problema em usar no Objective-C (baseado no C99), mas no código C89 genérico, você não pode usá-los.
Adam Rosenfield
Existe algum caminho a seguir? Por exemplo, obter o enum de volta com uma string?
Jameo
1
@ Jameso: Sim, mas não é tão simples como fazer uma pesquisa de matriz. Você precisará percorrer a FormatType_toString[]matriz e chamar -isEqualToString:cada elemento para encontrar uma correspondência ou usar um tipo de dados de mapeamento, como NSDictionarypara manter o mapa de pesquisa inversa.
Adam Rosenfield 17/02
1
O truque do Max O é bom em esquecer de adicionar entradas na FormatType_toStringmatriz.
AechoLiu
50

Minha solução:

edit: adicionei uma solução ainda melhor no final, usando o Modern Obj-C

1.
Coloque os nomes como chaves em uma matriz.
Verifique se os índices são as enumerações apropriadas e na ordem correta (caso contrário, exceção).
nota: names é uma propriedade sintetizada como * _names *;

o código não foi verificado para compilação, mas usei a mesma técnica no meu aplicativo.

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 o Obj-C moderno, podemos usar um dicionário para vincular descrições às chaves na enumeração.
A ordem NÃO 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 (em um método de instância de classe):

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


Yariv Nissim
fonte
12
Lembre-se de que toda vez que você liga +[typeDisplayNames], está recriando o dicionário. Isso é bom se for chamado apenas algumas vezes, mas se for chamado várias vezes, ficará muito caro. Uma solução melhor pode ser tornar o dicionário um singleton, para que ele seja criado apenas uma vez e permaneça na memória caso contrário. Memória clássica vs. enigma da CPU.
Joel Fischer
Ou altere-a para uma variável estática, por exemplo, static NSDictionary *dict = nil; if(!dict) dict = @{@(UserTypeParent): @"Parent"}; return dict;Comentários não permitirão quebras de linha, desculpe por isso.
Natanavra
29

Combinando a resposta @AdamRosenfield, o comentário @Christoph e outro truque para lidar com enums C simples, sugiro:

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

Na pior das hipóteses - como se você alterar o enum, mas se esquecer de alterar a matriz de nomes -, ele retornará nulo para essa chave.

Max O
fonte
12

defina typedef enum no cabeçalho da classe:

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

escreva um método como este na classe:

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

tem as strings dentro do arquivo Localizable.strings :

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

Eu usaria o token de string # do compilador (junto com macros para torná-lo mais 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
fonte
Isso funcionou muito bem em C99 - sou novo em C e achei que essa é a maneira mais limpa de realizar a pergunta. Também adicionei um padrão na minha implementação para itens que podem não ter sido definidos. Método muito limpo. Obrigado pelos resultados. Uso muito astuto de uma macro.
TravisWhidden
8

eu gosto de #define maneira de fazer isso:

// Coloque isso no seu arquivo .h, fora do bloco @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];
}

fonte (fonte não está mais disponível)

lindon fox
fonte
@ Daij-Djan, que tal voltar nilsearray.count <= enumValue ?
AnneBlue
@anneblue que capturaria o erro .. ainda seria frágil, porque se você adicionar um valor de enum OU o valor inteiro de um valor de enum mudar, isso dá errado. A resposta aceita seria bom
Daij-Djan
@codercat :( desculpe - não tenho certeza o que aconteceu com que o site não no caminho de volta quando máquina ou ....
lindon fox
Eu tenho uma pequena pergunta na resposta acima. Como converter o elemento string em kImageType. Eu preciso chamar o método imageTypeEnumToString passando a string. Pode me ajudar com o meu problema.
Ganesh
1
Eu gosto mais desta resposta, porque você tem as definições de string ao lado das enumerações. Menor chance de perder um valor. E o @Ganesh, para converter do valor bruto, poderia fazer isso: return (kImageType) [imageTypeArray indexOfObject: rawValue];
Harris
8

Eu fiz uma espécie de combinação de todas as soluções encontradas nesta página para criar as minhas, é um tipo de extensão de enumeração orientada a objetos ou algo assim.

De fato, se você precisar de mais do que constantes (ou seja, números inteiros), provavelmente precisará de um objeto de modelo (todos nós estamos falando sobre MVC, certo?)

Basta fazer a si mesmo a pergunta antes de usar isso, estou certo, de fato, você não precisa de um objeto de modelo real, inicializado a partir de um serviço da web, um plist, um banco de dados SQLite ou CoreData?

De qualquer forma, aqui vem o código (MPI é para "My Project Initials", todo mundo usa esse ou seu nome, ao que 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

E 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
fonte
parece bom, mas você está alocando e retornando dicionários completos quando precisa apenas de um de seus valores. Código VS VS Pretty? depende do que você quer e você ficará bem se não os usar tanto em seu código como em um loop enorme. Mas este será talvez útil com "dinâmico" ou-não codificados enums provenientes de um servidor, por exemplo
user2387149
5

Outra solução:

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

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

No seu método, você pode usar:

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

Resposta aprimorada @ yar1vn eliminando a dependência de string:

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

Assim, quando você alterar o nome da entrada do enum, a sequência correspondente será alterada. Útil no caso de você não mostrar essa string ao usuário.

Bohdan Orlov
fonte
Você pode explicar "- definir VariableName (arg) (@" "# arg) --- e, provavelmente, dar uma solução melhor?
xySVerma
Com #defines, quando você usa # para uma substituição, o argumento é envolvido automaticamente entre aspas duplas. Em C, quando duas cadeias aparecem próximas uma da outra no código como "foo""bar", isso resulta na cadeia "foobar"quando compilada. Então, #define VariableName(arg) (@""#arg)vai se expandir VariableName(MyEnum)para ser (@"""MyEnum"). Isso resultará na string @"MyEnum".
31817 Chris Douglass
3

Dada uma definição de enum como:

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

Podemos definir uma macro para converter um valor de enum para sua string correspondente, como mostrado abaixo.

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

A switchinstrução usada no bloco é para verificação de tipo e também para obter o suporte de preenchimento automático no Xcode.

insira a descrição da imagem aqui insira a descrição da imagem aqui

ylin0x81
fonte
2

Eu tinha um grande tipo enumerado que queria convertê-lo em uma NSDictionarypesquisa. Acabei usando seddo terminal OSX como:

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

que pode ser lido como: 'capture a primeira palavra na linha e produza @ (word): @ "word",'

Essa regex converte a enumeração em um arquivo de cabeçalho chamado 'ObservationType.h', que contém:

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

em algo como:

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

que pode ser agrupado em um método usando a sintaxe objetiva-c moderna @{ }(conforme explicado por @ yar1vn acima) para criar uma NSDictionarypesquisa:

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

A dispatch_onceplaca da caldeira é apenas para garantir que a variável estática seja inicializada de maneira segura para roscas.

Nota: Achei a expressão sed regex no OSX estranha - quando tentei +combinar 'um ou mais', não funcionou e tive que usar {1,}como substituto

Nick Ager
fonte
2

Eu uso uma variação na resposta de Barry Walk, que em ordem de importância:

  1. Permite que o compilador verifique se há cláusulas de maiúsculas e minúsculas ausentes (não é possível se você tiver uma cláusula padrão).
  2. Usa um nome típico de Objective-C (em vez de um nome semelhante ao Java).
  3. Gera uma exceção específica.
  4. É mais curto.

POR EXEMPLO:

- (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
fonte
2

A @pixel adicionou a resposta mais brilhante aqui: https://stackoverflow.com/a/24255387/1364257 Por favor, vote nele novamente!

Ele usa a macro X pura da década de 1960. (Eu mudei um pouco o código dele para o 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];
}

É isso aí. Limpo e arrumado. Graças a @pixel! https://stackoverflow.com/users/21804/pixel

voiger
fonte
@AlexandreG fornece sua solução, cara. É fácil ofender alguém. Esta solução tem seus prós e contras óbvios. Torne o mundo melhor com sua solução.
voiger
2

Eu combinei várias abordagens aqui. Gosto da ideia do pré-processador e da lista indexada.

Não há alocação dinâmica extra e, devido ao alinhamento, o compilador pode otimizar a pesquisa.

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

fonte
1

Primeiro de tudo, com relação ao FormatType.JSON: JSON não é um membro do FormatType, é um valor possível do tipo. FormatType nem é um tipo composto - é um escalar.

Segundo, a única maneira de fazer isso é criar uma tabela de mapeamento. A maneira mais comum de fazer isso no Objective-C é criar uma série de constantes se referindo aos seus "símbolos", assim você teria NSString *FormatTypeJSON = @"JSON"e assim por diante.

Mandril
fonte
1

o seguinte fornece uma solução para que a adição de uma nova enum exija apenas uma edição em uma linha, trabalho semelhante ao de adicionar uma única linha em uma lista de 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);
    }
}
fredwork
fonte
Essa técnica é chamada X-Macro, caso alguém queira ler sobre isso. Isso vem do fato de que, tradicionalmente, a macro FOR_EACH_GENDER () sempre era chamada apenas X (). Uma coisa que você pode querer fazer é #undef FOR_EACH_GENDER antes de redefini-la com um novo significado.
22417 uliwitness
1

Cada resposta aqui basicamente diz a mesma coisa, crie uma enum regular e use um getter personalizado para alternar entre as strings.

Emprego uma solução muito mais simples, mais rápida, mais curta e mais limpa - 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])

Então você pode simplesmente começar a digitar kNam... e o preenchimento automático exibirá as listas que você deseja!

Além disso, se você quiser manipular a lógica para todos os nomes de uma só vez, basta enumerar rapidamente a matriz literal na ordem, da seguinte maneira:

for (NSString *kName in kNames_allNames) {}

Por fim, a conversão do NSString nas macros garante um comportamento semelhante ao typedef!


Aproveitar!

Albert Renshaw
fonte
0

Muitas respostas, todas razoavelmente boas.

Se você procura uma solução genérica, Objective C, que usa algumas macros ...

O principal recurso é que ele usa a enumeração como um índice em uma matriz estática de constantes NSString. a própria matriz é agrupada em uma função para torná-la mais parecida com o conjunto de funções NSStringFromXXX prevalecentes nas APIs da Apple.

você precisará #import "NSStringFromEnum.h"encontrar aqui http://pastebin.com/u83RR3Vk

[EDIT] também precisa ser #import "SW+Variadic.h"encontrado aqui http://pastebin.com/UEqTzYLf

Exemplo 1: defina completamente um NEW enum typedef, com conversores de string.

em 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)

em 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) retorna @"chain_for_c"

  enum Dispatch_chain_cmd enumDispatch_chain_cmdFromNSString(NSString *value);

enumDispatch_chain_cmdFromNSString(@"chain_previous") retorna chain_previous

Exemplo 2: fornecer rotinas de conversão para uma enum existente também demonstra o uso de uma sequência de configurações e renomeando o nome do tipo usado nas funções.

em myfile.h


 #import "NSStringFromEnum.h"


 #define CAEdgeAntialiasingMask_SETTINGS_PARAMS CAEdgeAntialiasingMask,mask,EdgeMask,edgeMask

 interface_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS)

em 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)
não sincronizado
fonte
0

Aqui está trabalhando -> 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;
}
Nitin
fonte
desde resposta duplicado não é permitido, aqui é completa solução github.com/ndpiparava/ObjcEnumString
Nitin
-2

Dependendo das suas necessidades, você também pode usar as diretivas do compilador para simular o comportamento que está procurando.

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

Lembre-se das deficiências usuais do compilador (não digite seguro, a cópia direta e a colagem aumentam o arquivo de origem)

Alex Gosselin
fonte
8
Eu não acho que isso vai funcionar; em qualquer lugar do #defineestiver visível, você não será capaz de usar o valor enum real (ou seja, JSONserá substituído com @"JSON"pelo pré-processador e irá resultar em um erro do compilador ao atribuir a um FormatType.
Barry Wark