Converti typedef oggettivo-c nella sua stringa equivalente

Supponendo di avere un typedef dichiarato nel mio file .h in quanto tale:

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

Vorrei creare una funzione che converta il valore numerico del typedef in una stringa. Ad esempio, se è stato inviato il messaggio [self toString:JSON] ; ritornerebbe “JSON”.

La funzione sarebbe simile a questa:

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

Per inciso, se provo questa syntax

 [self toString:FormatType.JSON]; 

per passare il valore typedef al metodo, ottengo un errore. Cosa mi manca?

Questa è davvero una domanda in C, non specifica di Objective-C (che è un superset del linguaggio C). Le enumerazioni in C sono rappresentate come numeri interi. Quindi è necessario scrivere una funzione che restituisce una stringa dato un valore enum. Ci sono molti modi per farlo. Un array di stringhe tale che il valore enum può essere usato come un indice nell’array o una struttura di mappa (ad esempio un NSDictionary ) che mappa un valore enum su una stringa di lavoro, ma trovo che questi approcci non siano chiari come una funzione ciò rende esplicita la conversione (e l’approccio dell’array, sebbene il classico modo C sia pericoloso se i valori dell’enumerazione non sono contenti 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 domanda correlata sulla syntax corretta per un valore enum è che si utilizza solo il valore (ad es. JSON ), non la FormatType.JSON FormatType.JSON. FormatType è un tipo e i valori enum (ad es. JSON , XML , ecc.) Sono valori che è ansible assegnare a quel tipo.

Non puoi farlo facilmente. In C e Objective-C, le enumerazioni sono in realtà solo delle costanti intere glorificate. Dovrai generare da solo una tabella di nomi (o con qualche 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 mai l’enumerazione, devi ricordarti di cambiare l’array di nomi. Puoi risolvere questo problema con un abuso del preprocessore, ma è complicato e brutto.

Nota anche che questo presuppone che tu abbia 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 “passato massimo” nell’enumerazione o controllando se è inferiore alla lunghezza dell’array, sizeof(FormatType_toString) / sizeof(FormatType_toString[0]) .

La mia soluzione:

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

1.
Metti i nomi come chiavi in ​​un array.
Assicurati che gli indici siano l’enumerazione appropriata e nel giusto ordine (altrimenti eccezione).
nota: i nomi sono una proprietà sintetizzata come * _names *;

il codice non è stato controllato 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 possiamo usare un dizionario per bind le descrizioni alle chiavi nell’enum.
L’ordine non 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)]; } 

Utilizzo (in un metodo di istanza di class):

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

Combinando la risposta di @AdamRosenfield, il commento di @Christoph e un altro trucco per gestire semplici enumerati 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 modificassi l’enumerazione, ma dimentichi di cambiare la matrice dei nomi, restituirà nil per questa chiave.

definire typedef enum nell’intestazione della class:

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

scrivi un metodo come questo in class:

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

Userei il token stringa # del compilatore (insieme ai macro per renderlo 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 } 

Mi piace il modo #define di farlo:

// Inseriscilo nel file .h, all’esterno 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)

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

Infatti se hai bisogno di più di semplici costanti (cioè interi), probabilmente hai bisogno di un object modello (stiamo parlando di MVC, giusto?)

Basta porsi la domanda prima di usare questo, ho ragione, non hai, in effetti, bisogno di un vero object modello, inizializzato da un webservice, un plist, un database SQLite o CoreData?

Comunque qui 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  @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 

Migliorato @ yar1vn risposta eliminando la dipendenza da 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)]; } 

Così quando cambierai enum nome della voce la stringa corrispondente sarà cambiata. Utile nel caso in cui non hai intenzione di mostrare questa stringa all’utente.

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

Avevo un grande tipo enumerato che volevo convertire in una ricerca NSDictionary . Ho finito per usare sed dal terminale OSX come:

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

che può essere letto come segue: ‘cattura la prima parola sulla riga e visualizza @ (parola): @ “parola”,’

Questa 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 di simile:

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

che può quindi essere racchiuso in un metodo usando la syntax oggettiva-c @{ } (come spiegato da @ yar1vn sopra) per creare una ricerca NSDictionary :

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

La piastra della caldaia dispatch_once serve solo a garantire che la variabile statica sia inizializzata in modo thread-safe.

Nota: ho trovato l’espressione regex sed su dispari OSX – quando ho provato a usare + per abbinare ‘uno o più’ non ha funzionato e ho dovuto ricorrere all’utilizzo di {1,} come rimpiazzo

Uso una variazione sulla risposta di Barry Walk, quella in ordine di importanza:

  1. Consente al compilatore di verificare la presenza di clausole case mancanti (non è ansible 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ù breve

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

@ pixel ha aggiunto la risposta più brillante qui: https://stackoverflow.com/a/24255387/1364257 Per favore, invitalo!

Usa la nitida macro X degli anni ’60. (Ho cambiato un po ‘il suo codice per il moderno ObjC)

 #define X(a, b, c) ab, 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 pulito Grazie a @pixel! https://stackoverflow.com/users/21804/pixel

Ho combinato diversi approcci qui. Mi piace l’idea del preprocessore e della lista indicizzata.

Non ci sono allocazioni dinamiche extra e, a causa dell’inlining, 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]; } 

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

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

Quanto segue fornisce una soluzione tale che per aggiungere una nuova enumerazione è necessaria solo una modifica su una riga, lavoro simile all’aggiunta di una singola riga in una lista 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); } } 

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’istruzione switch utilizzata nel blocco serve per il controllo del tipo e anche per ottenere il supporto del completamento automatico in Xcode.

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

Molte risposte sono tutte abbastanza buone.

Se cerchi 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. la matrice stessa è avvolta in una funzione per renderla più simile alla suite delle funzioni NSStringFromXXX prevalenti nelle API Apple.

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

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

Esempio 1: definire completamente un nuovo 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) restituisce @"chain_for_c"

  enum Dispatch_chain_cmd enumDispatch_chain_cmdFromNSString(NSString *value); 

enumDispatch_chain_cmdFromNSString(@"chain_previous") restituisce chain_previous

Esempio 2: fornire le routine di conversione per un enum esistente dimostra anche l’uso di una stringa di impostazioni e rinominando il 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) 

Qui sta lavorando -> 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; } 

In base alle tue esigenze, potresti in alternativa utilizzare 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 rende il file sorgente più grande)