Converti typedef oggettivo-c nel suo equivalente di stringa


141

Supponendo che ho un typedef dichiarato nel mio file .h in quanto tale:

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

Vorrei costruire una funzione che converte il valore numerico del typedef in una stringa. Ad esempio, se il messaggio è [self toString:JSON]stato inviato; restituirebbe "JSON".

La funzione sarebbe simile a questa:

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

Per inciso, se provo questa sintassi

[self toString:FormatType.JSON];

per passare il valore typedef al metodo, viene visualizzato un errore. Cosa mi sto perdendo?


Vedi la mia risposta su stackoverflow.com/questions/6331762/enum-values-to-nsstring-ios per una soluzione più chiara al problema.
BooTooMany

4
Forse dovremmo abbracciare la lingua di Swift su enum.
Itachi,

@craig: ecco la soluzione github.com/ndpiparava/ObjcEnumString
Nitin

Per una soluzione senza utilizzare semplicemente un getter personalizzato per enum che si converte in stringa, controlla l'array di macro di tipo stringa: stackoverflow.com/a/53755377/2057171
Albert Renshaw,

Risposte:


135

Questa è davvero una domanda C, non specifica per Objective-C (che è un superset del linguaggio C). Gli enumerati in C sono rappresentati come numeri interi. Quindi è necessario scrivere una funzione che restituisce una stringa con un valore enum. Ci sono molti modi per farlo. Un array di stringhe in modo tale che il valore enum possa essere usato come indice nell'array o in una struttura di mappa (ad es. Un NSDictionary) che associa un valore enum a un lavoro a stringa, ma trovo che questi approcci non siano chiari come una funzione che rende esplicita la conversione (e l'approccio array, sebbene il Cmodo classico sia pericoloso se i valori enum non sono diversi da 0). Qualcosa del genere funzionerebbe:

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

La tua domanda correlata sulla sintassi corretta per un valore enum è che usi solo il valore (ad es. JSON), Non la FormatType.JSONsintassi. FormatTypeè un tipo e i valori enum (ad es JSON. XML, ecc.) sono valori che è possibile assegnare a quel tipo.


127

Non puoi farlo facilmente. In C e Objective-C, gli enum sono in realtà solo costanti intere glorificate. Dovrai generare una tabella di nomi tu stesso (o con un po 'di abuso del preprocessore). Per esempio:

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

Il pericolo di questo approccio è che se cambi l'enum, devi ricordare di cambiare l'array di nomi. Puoi risolvere questo problema con un po 'di abuso del preprocessore, ma è difficile e brutto.

Si noti inoltre che ciò presuppone che si disponga di una costante enum valida. Se si dispone di un valore intero da una fonte non attendibile, è inoltre necessario verificare che la costante sia valida, ad esempio includendo un valore "past max" nel proprio enum o verificando che sia inferiore alla lunghezza dell'array sizeof(FormatType_toString) / sizeof(FormatType_toString[0]).


37
è possibile inizializzare le matrici con indici espliciti, ad esempio string[] = { [XML] = "XML" }per assicurarsi che la stringa corrisponda correttamente agli enum
Christoph

@Christoph: Sì, questa è una funzione C99 chiamata inizializzatori designati . Va bene usare in Objective-C (che è basato su C99), ma per il codice C89 generico, non puoi usarli.
Adam Rosenfield,

C'è un modo per andare dall'altra parte? Ad esempio, ottenere l'enum restituito una stringa?
Jameo,

1
@Jameo: Sì, ma non è così semplice come fare una ricerca di array. Dovrai eseguire l'iterazione attraverso l' FormatType_toString[]array e chiamare -isEqualToString:ciascun elemento per trovare una corrispondenza, oppure utilizzare un tipo di dati di mapping come NSDictionaryper mantenere la mappa di ricerca inversa.
Adam Rosenfield,

1
Il trucco di Max O è buono per dimenticare di aggiungere voci nella FormatType_toStringmatrice.
AechoLiu,

50

La mia soluzione:

modifica: ho aggiunto anche una soluzione migliore alla fine, usando Modern Obj-C

1.
Inserisci i nomi come chiavi in ​​un array.
Assicurarsi che gli indici siano gli enum appropriati e nell'ordine corretto (altrimenti eccezione).
nota: nomi è una proprietà sintetizzata come * _names *;

il codice non è stato verificato per la compilazione, ma ho usato la stessa tecnica nella mia app.

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 puoi usare un dizionario per legare le descrizioni alle chiavi nell'enum.
L'ordine NON ha importanza .

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


Utilizzo (in un metodo di istanza di classe):

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



12
Tieni presente che ogni volta che chiami +[typeDisplayNames], stai ricreando il dizionario. Questo va bene se viene chiamato solo poche volte, ma se viene chiamato molte volte, diventerà molto costoso. Una soluzione migliore potrebbe essere quella di rendere il dizionario un singleton, quindi viene creato una sola volta e rimane in memoria altrimenti. Memoria classica contro enigma della CPU.
Joel Fischer,

O modificalo in una variabile statica, ad es. I static NSDictionary *dict = nil; if(!dict) dict = @{@(UserTypeParent): @"Parent"}; return dict;commenti non ti faranno interrompere la linea, mi dispiace per quello.
natanavra,

29

Combinando la risposta di @AdamRosenfield, il commento di @Christoph e un altro trucco per gestire i semplici en C suggerisco:

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

Nel peggiore dei casi - come se cambi l'enum ma dimentichi di cambiare l'array dei nomi - restituirà zero per questa chiave.


12

definire typedef enum nell'intestazione della classe:

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

scrivi un metodo come questo in classe:

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

avere le stringhe nel file Localizable.strings :

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

11

Vorrei usare il token stringa # del compilatore (insieme alle macro per rendere tutto più compatto):

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

Questo ha funzionato alla grande in C99 - Sono nuovo in C, e ho trovato che questo è il modo più pulito per realizzare la domanda posta. Ho anche aggiunto un valore predefinito nella mia implementazione per elementi che potrebbero non essere stati definiti. Metodo molto pulito. Grazie per i risultati Uso molto furbo di una macro.
TravisWidden

8

Mi piace il #definemodo di farlo:

// Inseriscilo nel tuo file .h, al di fuori del blocco @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 non più disponibile)


@ Daij-Djan che ne dici di tornare nilse array.count <= enumValue?
anneblue,

@anneblue che catturerebbe l'errore .. sarebbe comunque fragile perché se si aggiunge un valore enum O il valore intero di un valore enum cambia, questo va storto. La risposta accettata sarebbe buona
Daij-Djan

@codercat :( scusa, non sono sicuro di cosa sia successo a quel sito Web. Non nel modo in cui è tornato in macchina ...
lindon fox,

Ho una piccola domanda sulla risposta sopra. Come convertire l'elemento stringa in kImageType. Devo chiamare il metodo imageTypeEnumToString passando la stringa. Puoi aiutarmi per favore per il mio problema.
Ganesh,

1
Mi piace molto questa risposta, perché hai le definizioni di stringa proprio accanto agli enum. Minima possibilità di perdere un valore. E @Ganesh, per convertire da un valore non elaborato, potrebbe fare questo: return (kImageType) [imageTypeArray indexOfObject: rawValue];
Harris,

8

Ho creato una sorta di mix di tutte le soluzioni trovate in questa pagina per creare la mia, è una specie di estensione enum orientata agli oggetti o qualcosa del genere.

In effetti, se hai bisogno di qualcosa di più delle sole costanti (cioè numeri interi), probabilmente hai bisogno di un oggetto modello (stiamo tutti parlando di MVC, giusto?)

Ponetevi la domanda prima di usare questo, ho ragione, non avete davvero bisogno di un oggetto modello reale, inizializzato da un servizio web, un plist, un database SQLite o CoreData?

Comunque ecco che arriva il codice (MPI è per "My Project Initials", tutti usano questo o il loro nome, a quanto pare):

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

sembra carino, ma stai allocando e restituendo dizionari completi quando hai solo bisogno di uno dei suoi valori. Efficienza VS Codice grazioso? dipende da cosa vuoi e ti andrà bene se non li usi così tanto sul tuo codice come in un ciclo enorme. Ma questo sarà forse utile con enumerazioni "dinamiche" o non codificate provenienti da un server, ad esempio
user2387149

5

Un'altra soluzione:

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

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

Nel tuo metodo puoi usare:

NSString *operationCheck = AMZCartServiceOperationValue(operation);

4

Migliorata la risposta @ yar1vn eliminando la dipendenza dalla stringa:

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

Pertanto, quando cambierai il nome della voce enum, la stringa corrispondente verrà modificata. Utile nel caso in cui non si desideri mostrare questa stringa all'utente.


Puoi spiegare "- define VariableName (arg) (@" "# arg) --- e probabilmente dare una soluzione migliore?
xySVerma

Con #defines, quando si utilizza # per una sostituzione, l'argomento viene automaticamente racchiuso tra virgolette doppie. In C, quando due stringhe appaiono una accanto all'altra in un codice simile "foo""bar", risulta una stringa una "foobar"volta compilata. Quindi, #define VariableName(arg) (@""#arg)si espanderà VariableName(MyEnum)per essere (@"""MyEnum"). Ciò comporterà la stringa @"MyEnum".
Chris Douglass,

3

Data una definizione enum come:

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

Possiamo definire una macro per convertire un valore enum nella stringa corrispondente, come mostrato di seguito.

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

L' switchistruzione utilizzata nel blocco è per il controllo del tipo e anche per ottenere il supporto del completamento automatico in Xcode.

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine


2

Avevo un tipo numerato di grandi dimensioni che volevo convertirlo in una NSDictionaryricerca. Ho finito per usare seddal terminale OSX come:

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

che può essere letto come: "cattura la prima parola sulla riga e genera @ (parola): @" parola ","

Questo regex converte l'enum in un file di intestazione denominato 'ObservationType.h' che contiene:

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

in qualcosa del tipo:

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

che può quindi essere racchiuso in un metodo utilizzando la moderna sintassi obiettivo-c @{ }(come spiegato da @ yar1vn sopra) per creare una NSDictionaryricerca:

-(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_oncepiastra della caldaia serve solo a garantire che la variabile statica sia inizializzata in modo sicuro per il thread.

Nota: ho trovato l'espressione sed regex su OSX dispari - quando ho cercato di utilizzare +per abbinare 'uno o più' non ha funzionato e ho dovuto ricorrere all'uso {1,}come sostituto


2

Uso una variante della risposta di Barry Walk, che in ordine di importanza:

  1. Consente al compilatore di verificare la presenza di clausole case mancanti (non è possibile se si dispone di una clausola predefinita).
  2. Utilizza un nome tipico Objective-C (piuttosto che un nome simile a Java).
  3. Solleva un'eccezione specifica.
  4. È più corto

PER ESEMPIO:

- (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 ha aggiunto la risposta più brillante qui: https://stackoverflow.com/a/24255387/1364257 Per favore, votalo!

Usa la macro X ordinata degli anni '60. (Ho cambiato un po 'il suo codice per il moderno 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];
}

Questo è tutto. Pulito e ordinato. Grazie a @pixel! https://stackoverflow.com/users/21804/pixel


@AlexandreG fornisci la tua soluzione, amico. È facile carpire qualcuno. Questa soluzione ha i suoi ovvi vantaggi e i suoi ovvi vantaggi. Rendi il mondo migliore con la tua soluzione.
Voiger,

2

Ho combinato diversi approcci qui. Mi piace l'idea del preprocessore e l'elenco indicizzato.

Non esiste un'allocazione dinamica aggiuntiva e, a causa dell'inline, il compilatore potrebbe essere in grado di ottimizzare la ricerca.

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

Prima di tutto, per quanto riguarda FormatType.JSON: JSON non è un membro di FormatType, è un possibile valore del tipo. FormatType non è nemmeno un tipo composito, è uno scalare.

In secondo luogo, l'unico modo per farlo è creare una tabella di mappatura. Il modo più comune per farlo in Objective-C è creare una serie di costanti che si riferiscono ai tuoi "simboli", così avresti NSString *FormatTypeJSON = @"JSON"e così via.


1

Di seguito viene fornita una soluzione tale che per aggiungere un nuovo enum è necessaria solo una modifica di una riga, un lavoro simile all'aggiunta di una singola riga in un elenco di 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);
    }
}

Questa tecnica si chiama X-Macro, nel caso qualcuno volesse leggerne. Ciò deriva dal fatto che, tradizionalmente, la macro FOR_EACH_GENDER () era sempre chiamata semplicemente X (). Una cosa che potresti voler fare è #undef FOR_EACH_GENDER prima di ridefinirlo con un nuovo significato.
uliwitness il

1

Ogni risposta qui dice sostanzialmente la stessa cosa, crea un enum regolare e quindi usa un getter personalizzato per passare da una stringa all'altra.

Uso una soluzione molto più semplice, più veloce, più breve e più pulita, utilizzando i macro!


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

Quindi puoi semplicemente iniziare a digitare kNam...e il completamento automatico visualizzerà gli elenchi che desideri!

Inoltre, se si desidera gestire la logica per tutti i nomi contemporaneamente, è possibile semplicemente enumerare rapidamente l'array letterale in ordine, come segue:

for (NSString *kName in kNames_allNames) {}

Infine, il cast di NSString nelle macro assicura un comportamento simile a typedef!


Godere!


0

Molte risposte tutte abbastanza buone.

Se stai cercando una soluzione generica, Objective C che utilizza alcune macro ...

La caratteristica chiave è che usa l'enum come indice in una matrice statica di costanti NSString. l'array stesso è racchiuso in una funzione per renderlo più simile alla suite di funzioni NSStringFromXXX prevalente nelle API di Apple.

dovrai #import "NSStringFromEnum.h"trovarlo qui http://pastebin.com/u83RR3Vk

[EDIT] deve essere #import "SW+Variadic.h"trovato anche qui http://pastebin.com/UEqTzYLf

Esempio 1: definire completamente un NEW enum typedef, con convertitori di stringhe.

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

in myfile.m:


 #import "myfile.h"

 implementation_NSString_Enum_Converters(Dispatch_chain_cmd)

usare :

NSString *NSStringFromEnumDispatch_chain_cmd(enum Dispatch_chain_cmd value);

NSStringFromEnumDispatch_chain_cmd(chain_for_c) ritorna @"chain_for_c"

  enum Dispatch_chain_cmd enumDispatch_chain_cmdFromNSString(NSString *value);

enumDispatch_chain_cmdFromNSString(@"chain_previous") ritorna chain_previous

Esempio 2: fornire routine di conversione per un enum esistente dimostra anche l'utilizzo di una stringa di impostazioni e la ridenominazione del nome tipografico utilizzato nelle funzioni.

in myfile.h


 #import "NSStringFromEnum.h"


 #define CAEdgeAntialiasingMask_SETTINGS_PARAMS CAEdgeAntialiasingMask,mask,EdgeMask,edgeMask

 interface_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS)

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

0

Qui funziona -> 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;
}

poiché non è consentita la risposta duplicata, ecco la soluzione completa github.com/ndpiparava/ObjcEnumString
Nitin

-2

A seconda delle tue esigenze, puoi in alternativa usare le direttive del compilatore per simulare il comportamento che stai cercando.

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

Basta ricordare le solite carenze del compilatore, (non digitare sicuro, copia-incolla diretta ingrandisce il file sorgente)


8
Non penso che funzionerà; ovunque #definesia visibile, non sarai in grado di utilizzare il valore enum effettivo (ovvero JSONverrà sostituito @"JSON"dal preprocessore e comporterà un errore del compilatore durante l'assegnazione a un FormatType.
Barry Wark
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.