Convertir object-c typedef en son équivalent chaîne

141

En supposant que j'ai un typedef déclaré dans mon fichier .h comme tel:

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

Je voudrais créer une fonction qui convertit la valeur numérique du typedef en une chaîne. Par exemple, si le message a [self toString:JSON]été envoyé; il renverrait «JSON».

La fonction ressemblerait à ceci:

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

Incidemment, si j'essaye cette syntaxe

[self toString:FormatType.JSON];

pour passer la valeur typedef à la méthode, j'obtiens une erreur. Qu'est-ce que je rate?

Craig
la source
Voir ma réponse sur stackoverflow.com/questions/6331762/enum-values-to-nsstring-ios pour une solution plus propre au problème.
BooTooMany
4
Peut-être devrions-nous donner un câlin au langage Swift sur enum.
Itachi
@craig: voici la solution github.com/ndpiparava/ObjcEnumString
Nitin
Pour une solution sans simplement utiliser un getter personnalisé pour une énumération qui se convertit en chaîne, consultez le tableau de macros de conversion de chaînes: stackoverflow.com/a/53755377/2057171
Albert Renshaw

Réponses:

135

C'est vraiment une question C, non spécifique à Objective-C (qui est un sur-ensemble du langage C). Les énumérations en C sont représentées sous forme d'entiers. Vous devez donc écrire une fonction qui renvoie une chaîne avec une valeur d'énumération. Il existe de nombreuses façons de procéder. Un tableau de chaînes tel que la valeur enum peut être utilisée comme un index dans le tableau ou une structure de carte (par exemple un NSDictionary) qui mappe une valeur enum à une chaîne de travail, mais je trouve que ces approches ne sont pas aussi claires qu'une fonction qui rend la conversion explicite (et l'approche tableau, bien que la Cméthode classique soit dangereuse si vos valeurs d'énumération ne sont pas contingentes à partir de 0). Quelque chose comme ça fonctionnerait:

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

Votre question relative à la syntaxe correcte d'une valeur enum est que vous n'utilisez que la valeur (par exemple JSON), pas la FormatType.JSONsyntaxe. FormatTypeest un type et les valeurs de ENUM (par exemple JSON, XML, etc.) sont des valeurs que vous pouvez attribuer à ce type.

Barry Wark
la source
127

Vous ne pouvez pas le faire facilement. En C et Objective-C, les énumérations ne sont en réalité que des constantes entières glorifiées. Vous devrez générer vous-même une table de noms (ou avec un abus de préprocesseur). Par exemple:

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

Le danger de cette approche est que si jamais vous changez l'énumération, vous devez vous rappeler de changer le tableau de noms. Vous pouvez résoudre ce problème avec un abus de préprocesseur, mais c'est délicat et laid.

Notez également que cela suppose que vous disposez d'une constante d'énumération valide. Si vous avez une valeur entière à partir d' une source non fiable, vous devez en plus de faire une vérification que votre constante est valable, par exemple en incluant une valeur « passé max » dans votre ENUM, ou en vérifiant si elle est inférieure à la longueur du tableau, sizeof(FormatType_toString) / sizeof(FormatType_toString[0]).

Adam Rosenfield
la source
37
vous pouvez initialiser des tableaux avec des indices explicites, par exemple string[] = { [XML] = "XML" }pour vous assurer que la chaîne correspond correctement
Christoph
@Christoph: Oui, c'est une fonctionnalité C99 appelée initialiseurs désignés . C'est bien à utiliser en Objective-C (qui est basé sur C99), mais pour le code C89 générique, vous ne pouvez pas les utiliser.
Adam Rosenfield
Y a-t-il moyen d'aller dans l'autre sens? Par exemple, récupérez l'énumération avec une chaîne?
Jameo
1
@Jameo: Oui, mais ce n'est pas aussi simple que de faire une recherche de tableau. Vous devrez soit parcourir le FormatType_toString[]tableau et appeler -isEqualToString:chaque élément pour trouver une correspondance, soit utiliser un type de données de mappage tel que NSDictionarypour maintenir la carte de recherche inverse.
Adam Rosenfield
1
L'astuce de Max O consiste à oublier d'ajouter des entrées dans le FormatType_toStringtableau.
AechoLiu
50

Ma solution:

modifier: j'ai ajouté une solution encore meilleure à la fin, en utilisant Modern Obj-C

1.
Mettez les noms sous forme de clés dans un tableau.
Assurez-vous que les index sont les énumérations appropriées et dans le bon ordre (sinon exception).
note: names est une propriété synthétisée sous forme de * _names *;

le code n'a pas été vérifié pour la compilation, mais j'ai utilisé la même technique dans mon application.

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.
En utilisant Modern Obj-C, nous pouvons utiliser un dictionnaire pour lier les descriptions aux clés de l'énumération.
L'ordre n'a pas d'importance .

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


Utilisation (dans une méthode d'instance de classe):

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


Yariv Nissim
la source
12
Sachez que chaque fois que vous appelez +[typeDisplayNames], vous recréez le dictionnaire. C'est très bien si elle n'est appelée que quelques fois, mais si elle est appelée plusieurs fois, cela coûtera très cher. Une meilleure solution peut être de faire du dictionnaire un singleton, de sorte qu'il ne soit créé qu'une seule fois et reste en mémoire sinon. Mémoire classique contre énigme CPU.
Joel Fischer
Ou changez-le en une variable statique, par exemple, les static NSDictionary *dict = nil; if(!dict) dict = @{@(UserTypeParent): @"Parent"}; return dict;commentaires ne vous laisseront pas de saut de ligne, désolé pour cela.
natanavra
29

En combinant la réponse @AdamRosenfield, le commentaire @Christoph et une autre astuce pour gérer les énumérations C simples, je suggère:

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

Dans le pire des cas - comme si vous changez l'énumération mais oubliez de changer le tableau des noms - il renverra nil pour cette clé.

Max O
la source
12

définir typedef enum dans l'en-tête de classe:

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

écrivez une méthode comme celle-ci en classe:

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

avoir les chaînes dans le fichier Localizable.strings :

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

J'utiliserais le jeton # string du compilateur (avec des macros pour le rendre plus compact):

#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
la source
Cela a très bien fonctionné en C99 - je suis nouveau en C, et j'ai trouvé que c'était le moyen le plus propre de répondre à la question posée. J'ai également ajouté une valeur par défaut dans mon implémentation pour les éléments qui n'ont peut-être pas été définis. Méthode très propre. Merci pour les résultats. Utilisation très astucieuse d'une macro.
TravisWhidden
8

J'aime la #definefaçon de procéder:

// Placez ceci dans votre fichier .h, en dehors du bloc @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];
}

source (la source n'est plus disponible)

renard lindon
la source
@ Daij-Djan qu'en est-il de revenir nilsi array.count <= enumValue?
anneblue
@anneblue qui attraperait l'erreur .. cela resterait fragile car si vous ajoutez une valeur enum OU la valeur entière d'une valeur enum change, cela se passe mal. La réponse acceptée serait bonne
Daij-Djan
@codercat :( désolé - je ne sais pas ce qui est arrivé à ce site Web. Pas dans le chemin du retour Quand la machine non plus ...
lindon fox
J'ai une petite question sur la réponse ci-dessus. Comment convertir un élément de chaîne en kImageType. J'ai besoin d'appeler la méthode imageTypeEnumToString en passant la chaîne.Pouvez-vous s'il vous plaît m'aider pour mon problème.
Ganesh
1
J'aime mieux cette réponse, car vous avez les définitions de chaîne juste à côté des énumérations. Moins de chance de manquer une valeur. Et @Ganesh, pour convertir à partir de la valeur brute, pourrait faire ceci: return (kImageType) [imageTypeArray indexOfObject: rawValue];
Harris
8

J'ai fait une sorte de mélange de toutes les solutions trouvées sur cette page pour créer la mienne, c'est une sorte d'extension enum orientée objet ou quelque chose comme ça.

En fait, si vous avez besoin de plus que de simples constantes (c'est-à-dire des entiers), vous avez probablement besoin d'un objet modèle (nous parlons tous de MVC, non?)

Posez-vous simplement la question avant d'utiliser ceci, ai-je raison, n'avez-vous pas en fait besoin d'un objet modèle réel, initialisé à partir d'un webservice, d'un plist, d'une base de données SQLite ou de CoreData?

Quoi qu'il en soit, voici le code (MPI est pour "My Project Initials", tout le monde utilise tel ou tel nom, semble-t-il):

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

Et 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
la source
semble bien, mais vous allouez et renvoyez des dictionnaires complets lorsque vous n'avez besoin que d'une de ses valeurs. Efficacité VS joli code? dépend de ce que vous voulez et vous serez d'accord avec cela si vous ne les utilisez pas beaucoup sur votre code comme dans une énorme boucle. Mais cela sera peut-être utile avec des
énumérations
5

Une autre solution:

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

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

Dans votre méthode, vous pouvez utiliser:

NSString *operationCheck = AMZCartServiceOperationValue(operation);
Kennymuse
la source
4

Réponse @ yar1vn améliorée en supprimant la dépendance de chaîne:

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

Ainsi, lorsque vous modifierez le nom de l'entrée d'énumération, la chaîne correspondante sera modifiée. Utile si vous n'allez pas montrer cette chaîne à l'utilisateur.

Bohdan Orlov
la source
Pouvez-vous expliquer "- définir NomVariable (arg) (@" "# arg) --- et probablement donner une meilleure solution?
xySVerma
Avec #defines, lorsque vous utilisez # pour une substitution, l'argument est automatiquement entouré de guillemets doubles. En C, lorsque deux chaînes s'affichent l'une à côté de l'autre dans du code comme "foo""bar", il en résulte la chaîne "foobar"lors de la compilation. Donc, #define VariableName(arg) (@""#arg)va se développer VariableName(MyEnum)pour être (@"""MyEnum"). Cela se traduira par la chaîne @"MyEnum".
Chris Douglass
3

Étant donné une définition enum comme:

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

Nous pouvons définir une macro pour convertir une valeur enum en sa chaîne correspondante, comme indiqué ci-dessous.

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

L' switchinstruction utilisée dans le bloc est pour la vérification de type, ainsi que pour obtenir la prise en charge de la saisie semi-automatique dans Xcode.

entrez la description de l'image ici entrez la description de l'image ici

ylin0x81
la source
2

J'avais un grand type énuméré que je voulais convertir en une NSDictionaryrecherche. J'ai fini par utiliser seddepuis le terminal OSX comme:

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

qui peut être lu comme: 'capturer le premier mot sur la ligne et afficher @ (mot): @ "mot",'

Cette regex convertit l'énumération dans un fichier d'en-tête nommé 'ObservationType.h' qui contient:

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

en quelque chose comme:

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

qui peut ensuite être enveloppé dans une méthode utilisant la syntaxe objective-c moderne @{ }(comme expliqué par @ yar1vn ci-dessus) pour créer une NSDictionaryrecherche:

-(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_onceplaque chauffante sert uniquement à garantir que la variable statique est initialisée de manière thread-safe.

Remarque: j'ai trouvé l'expression regex sed sur OSX étrange - lorsque j'ai essayé d'utiliser +pour correspondre à `` un ou plusieurs '', cela ne fonctionnait pas et j'ai dû recourir à l'utilisation {1,}en remplacement

Nick Ager
la source
2

J'utilise une variante de la réponse de Barry Walk, qui, par ordre d'importance:

  1. Permet au compilateur de vérifier les clauses case manquantes (il ne peut pas si vous avez une clause par défaut).
  2. Utilise un nom typique Objective-C (plutôt qu'un nom de type Java).
  3. Déclenche une exception spécifique.
  4. Est plus court.

PAR EXEMPLE:

- (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
la source
2

@pixel a ajouté la réponse la plus brillante ici: https://stackoverflow.com/a/24255387/1364257 S'il vous plaît, votez pour lui!

Il utilise la macro X soignée des années 1960. (J'ai un peu changé son code pour l'ObjC moderne)

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

C'est tout. Propre et soigné. Merci à @pixel! https://stackoverflow.com/users/21804/pixel

voiger
la source
@AlexandreG fournit votre solution, mec. Il est facile de caresser quelqu'un. Cette solution a ses avantages et ses inconvénients évidents. Rendez le monde meilleur avec votre solution.
voiger
2

J'ai combiné plusieurs approches ici. J'aime l'idée du préprocesseur et la liste indexée.

Il n'y a pas d'allocation dynamique supplémentaire, et en raison de l'inlining, le compilateur pourrait être en mesure d'optimiser la recherche.

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

la source
1

Tout d'abord, en ce qui concerne FormatType.JSON: JSON n'est pas membre de FormatType, c'est une valeur possible du type. FormatType n'est même pas un type composite - c'est un scalaire.

Deuxièmement, la seule façon de procéder est de créer une table de mappage. La façon la plus courante de faire cela en Objective-C est de créer une série de constantes faisant référence à vos "symboles", donc vous auriezNSString *FormatTypeJSON = @"JSON" , etc.

Mandrin
la source
1

ce qui suit fournit une solution telle que l'ajout d'une nouvelle énumération ne nécessite qu'une modification sur une ligne, un travail similaire à l'ajout d'une seule ligne dans une liste 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
la source
Cette technique s'appelle X-Macro, au cas où quelqu'un voudrait en savoir plus. Cela vient du fait que, traditionnellement, la macro FOR_EACH_GENDER () était toujours juste appelée X (). Une chose que vous voudrez peut-être faire est de #undef FOR_EACH_GENDER avant de le redéfinir avec une nouvelle signification.
uliwitness
1

Chaque réponse ici dit essentiellement la même chose, créez une énumération régulière, puis utilisez un getter personnalisé pour basculer entre les chaînes.

J'emploie une solution beaucoup plus simple qui est plus rapide, plus courte et plus propre - en utilisant des 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])

Ensuite, vous pouvez simplement commencer à taper kNam... et la saisie semi-automatique affichera les listes que vous désirez!

De plus, si vous souhaitez gérer la logique de tous les noms à la fois, vous pouvez simplement énumérer rapidement le tableau littéral dans l'ordre, comme suit:

for (NSString *kName in kNames_allNames) {}

Enfin, le casting NSString dans les macros garantit un comportement similaire à typedef!


Prendre plaisir!

Albert Renshaw
la source
0

De nombreuses réponses sont toutes assez bonnes.

Si vous recherchez une solution Objective C générique qui utilise certaines macros ...

La caractéristique clé est qu'elle utilise l'énumération comme index dans un tableau statique de constantes NSString. le tableau lui-même est enveloppé dans une fonction pour le faire ressembler davantage à la suite de fonctions NSStringFromXXX répandue dans les API Apple.

vous devrez #import "NSStringFromEnum.h"trouver ici http://pastebin.com/u83RR3Vk

[EDIT] doit également être #import "SW+Variadic.h"trouvé ici http://pastebin.com/UEqTzYLf

Exemple 1: définissez complètement un NOUVEAU typedef enum, avec des convertisseurs de chaîne.

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

dans myfile.m:


 #import "myfile.h"

 implementation_NSString_Enum_Converters(Dispatch_chain_cmd)

utiliser :

NSString *NSStringFromEnumDispatch_chain_cmd(enum Dispatch_chain_cmd value);

NSStringFromEnumDispatch_chain_cmd(chain_for_c) Retour @"chain_for_c"

  enum Dispatch_chain_cmd enumDispatch_chain_cmdFromNSString(NSString *value);

enumDispatch_chain_cmdFromNSString(@"chain_previous") Retour chain_previous

Exemple 2: fournir des routines de conversion pour une énumération existante montre également l'utilisation d'une chaîne de paramètres et le changement de nom du type utilisé dans les fonctions.

dans myfile.h


 #import "NSStringFromEnum.h"


 #define CAEdgeAntialiasingMask_SETTINGS_PARAMS CAEdgeAntialiasingMask,mask,EdgeMask,edgeMask

 interface_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS)

dans 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)
non synchronisé
la source
0

Ici fonctionne -> 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;
}
Nitine
la source
puisque la réponse en double n'est pas autorisée, voici la solution complète github.com/ndpiparava/ObjcEnumString
Nitin
-2

En fonction de vos besoins, vous pouvez également utiliser les directives du compilateur pour simuler le comportement que vous recherchez.

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

Rappelez-vous simplement les défauts habituels du compilateur (le type n'est pas sûr, le copier-coller direct agrandit le fichier source)

Alex Gosselin
la source
8
Je ne pense pas que cela fonctionnera; n'importe où le#define est visible, vous ne pourrez pas utiliser la valeur d'énumération réelle (c.-à JSON- d. sera remplacé @"JSON"par le préprocesseur et entraînera une erreur du compilateur lors de l'assignation à un FormatType.
Barry Wark