Konvertieren Sie Objective-C Typedef in sein String-Äquivalent

141

Angenommen, ich habe ein typedef in meiner .h-Datei als solches deklariert:

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

Ich möchte eine Funktion erstellen, die den numerischen Wert des typedef in eine Zeichenfolge konvertiert. Zum Beispiel, wenn die Nachricht [self toString:JSON]gesendet wurde; es würde 'JSON' zurückgeben.

Die Funktion würde ungefähr so ​​aussehen:

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

Übrigens, wenn ich diese Syntax versuche

[self toString:FormatType.JSON];

Um den typedef-Wert an die Methode zu übergeben, wird eine Fehlermeldung angezeigt. Was vermisse ich?

Craig
quelle
Eine sauberere Lösung des Problems finden Sie in meiner Antwort unter stackoverflow.com/questions/6331762/enum-values-to-nsstring-ios .
BooTooMany
4
Vielleicht sollten wir Swift Sprache auf enum umarmen .
Itachi
@craig: hier ist Lösung github.com/ndpiparava/ObjcEnumString
Nitin
Eine Lösung ohne die Verwendung eines benutzerdefinierten Getters für Enum, der in einen String konvertiert werden kann, finden Sie im String-Cast-Makro-Array: stackoverflow.com/a/53755377/2057171
Albert Renshaw,

Antworten:

135

Dies ist wirklich eine C-Frage, die nicht spezifisch für Objective-C ist (eine Obermenge der C-Sprache). Aufzählungen in C werden als Ganzzahlen dargestellt. Sie müssen also eine Funktion schreiben, die eine Zeichenfolge mit einem Aufzählungswert zurückgibt. Es gibt viele Möglichkeiten, dies zu tun. Ein Array von Zeichenfolgen, so dass der Aufzählungswert als Index für das Array verwendet werden kann, oder eine Zuordnungsstruktur (z. B. eine NSDictionary), die einen Aufzählungswert einer Zeichenfolge zuordnet, funktioniert, aber ich finde, dass diese Ansätze nicht so klar sind wie eine Funktion, die macht die Konvertierung explizit (und den Array-Ansatz, obwohl der klassische CWeg gefährlich ist, wenn Ihre Enum-Werte nicht von 0 abweichen). So etwas würde funktionieren:

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

Ihre verwandte Frage zur korrekten Syntax für einen Enum-Wert lautet, dass Sie nur den Wert (z. B. JSON) und nicht die FormatType.JSONSytax verwenden. FormatTypeein Typ ist und die ENUM - Werte (zB JSON, XMLusw.) sind Werte , die Sie zu diesem Typ zuordnen.

Barry Wark
quelle
127

Sie können es nicht einfach machen. In C und Objective-C sind Aufzählungen wirklich nur verherrlichte ganzzahlige Konstanten. Sie müssen selbst eine Namenstabelle erstellen (oder bei einem Präprozessor-Missbrauch). Beispielsweise:

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

Die Gefahr dieses Ansatzes besteht darin, dass Sie, wenn Sie jemals die Aufzählung ändern, daran denken müssen, das Array von Namen zu ändern. Sie können dieses Problem mit einem Missbrauch des Präprozessors lösen, aber es ist schwierig und hässlich.

Beachten Sie auch, dass dies voraussetzt, dass Sie eine gültige Aufzählungskonstante haben. Wenn Sie einen ganzzahligen Wert aus einer nicht vertrauenswürdigen Quelle haben, müssen Sie zusätzlich überprüfen, ob Ihre Konstante gültig ist, z. B. indem Sie einen Wert "past max" in Ihre Aufzählung aufnehmen oder prüfen, ob er kleiner als die Arraylänge ist sizeof(FormatType_toString) / sizeof(FormatType_toString[0]).

Adam Rosenfield
quelle
37
Sie können Arrays mit expliziten Indizes initialisieren, z. B. string[] = { [XML] = "XML" }um sicherzustellen, dass die Zeichenfolge ordnungsgemäß mit den Aufzählungen übereinstimmt
Christoph
@Christoph: Ja, das ist eine C99-Funktion, die als designierte Initialisierer bezeichnet wird . Die Verwendung in Objective-C (das auf C99 basiert) ist in Ordnung, aber für generischen C89-Code können Sie diese nicht verwenden.
Adam Rosenfield
Gibt es einen Weg in die andere Richtung? Erhalten Sie zum Beispiel die Aufzählung mit einer Zeichenfolge zurück?
Jameo
1
@Jameo: Ja, aber es ist nicht ganz so einfach wie eine Array-Suche. Sie müssen entweder das FormatType_toString[]Array durchlaufen und -isEqualToString:jedes Element aufrufen , um eine Übereinstimmung zu finden, oder einen Zuordnungsdatentyp verwenden NSDictionary, um beispielsweise die inverse Lookup-Zuordnung beizubehalten.
Adam Rosenfield
1
Der Trick von Max O besteht darin, das Hinzufügen von Einträgen im FormatType_toStringArray zu vergessen .
AechoLiu
50

Meine Lösung:

Bearbeiten: Ich habe am Ende mit Modern Obj-C noch eine bessere Lösung hinzugefügt.

1. Fügen
Sie Namen als Schlüssel in ein Array ein.
Stellen Sie sicher, dass die Indizes die entsprechenden Aufzählungen und in der richtigen Reihenfolge sind (ansonsten Ausnahme).
Hinweis: names ist eine Eigenschaft, die als * _names * synthetisiert wird.

Code wurde nicht für die Kompilierung überprüft, aber ich habe die gleiche Technik in meiner App verwendet.

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.
Mit Modern Obj-C können Sie ein Wörterbuch verwenden, um Beschreibungen mit Schlüsseln in der Aufzählung zu verknüpfen.
Bestellung spielt keine Rolle .

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


Verwendung (in einer Klasseninstanzmethode):

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


Yariv Nissim
quelle
12
Beachten Sie +[typeDisplayNames], dass Sie das Wörterbuch bei jedem Anruf neu erstellen. Dies ist in Ordnung, wenn es nur einige Male aufgerufen wird, aber wenn es viele Male aufgerufen wird, wird dies sehr teuer. Eine bessere Lösung könnte darin bestehen, das Wörterbuch zu einem Singleton zu machen, sodass es nur einmal erstellt wird und ansonsten im Speicher bleibt. Klassisches Speicher-gegen-CPU-Rätsel.
Joel Fischer
Oder ändern Sie es in eine statische Variable, z. B. können Sie in den static NSDictionary *dict = nil; if(!dict) dict = @{@(UserTypeParent): @"Parent"}; return dict;Kommentaren keine Zeilenumbrüche vornehmen. Tut mir leid.
Natanavra
29

Ich schlage vor, die Antwort von @AdamRosenfield, den Kommentar von @Christoph und einen weiteren Trick zu kombinieren, um mit einfachen C-Aufzählungen umzugehen:

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

Im schlimmsten Fall - wenn Sie beispielsweise die Aufzählung ändern, aber vergessen, das Namensarray zu ändern - wird für diesen Schlüssel null zurückgegeben.

Max O.
quelle
12

Definieren Sie typedef enum im Klassenheader:

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

Schreiben Sie eine solche Methode in der Klasse:

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

Haben Sie die Zeichenfolgen in der Datei Localizable.strings :

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

Ich würde das # String-Token des Compilers verwenden (zusammen mit Makros, um alles kompakter zu machen):

#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
quelle
Dies hat in C99 hervorragend funktioniert - ich bin neu bei C und fand, dass dies der sauberste Weg ist, um die gestellte Frage zu beantworten. Ich habe in meiner Implementierung auch einen Standard für Elemente hinzugefügt, die möglicherweise nicht definiert wurden. Sehr saubere Methode. Danke für die Ergebnisse. Sehr geschickte Verwendung eines Makros.
TravisWhidden
8

Ich mag die #defineArt und Weise, dies zu tun:

// Platziere dies in deiner .h-Datei außerhalb des @ interface-Blocks

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

Quelle (Quelle nicht mehr verfügbar)

Lindon Fox
quelle
@ Daij-Djan was ist mit der Rückkehr, nilwenn array.count <= enumValue?
Anneblue
@anneblue, das den Fehler abfangen würde. Es wäre immer noch zerbrechlich, denn wenn Sie einen Aufzählungswert hinzufügen ODER sich der ganzzahlige Wert eines Aufzählungswerts ändert, geht dies schief. Die akzeptierte Antwort wäre gut
Daij-Djan
@codercat :( Entschuldigung - nicht sicher, was mit dieser Website passiert ist. Nicht auf dem Weg zurück Wenn Maschine auch ...
Lindsay Fox
Ich habe eine kleine Frage zu der obigen Antwort. So konvertieren Sie ein Zeichenfolgenelement in kImageType. Ich muss die imageTypeEnumToString-Methode aufrufen, indem ich den String übergebe. Können Sie mir bitte bei meinem Problem helfen?
Ganesh
1
Diese Antwort gefällt mir am besten, weil Sie die Zeichenfolgendefinitionen direkt neben den Aufzählungen haben. Geringste Chance, einen Wert zu verpassen. Und @Ganesh, um vom Rohwert zu konvertieren, könnte dies tun: return (kImageType) [imageTypeArray indexOfObject: rawValue];
Harris
8

Ich habe eine Art Mix aus allen auf dieser Seite gefundenen Lösungen erstellt, um meine zu erstellen. Es ist eine Art objektorientierte Enumerweiterung oder so.

Wenn Sie mehr als nur Konstanten (dh ganze Zahlen) benötigen, benötigen Sie wahrscheinlich ein Modellobjekt (wir sprechen alle über MVC, oder?)

Stellen Sie sich einfach die Frage, bevor Sie diese verwenden. Habe ich Recht? Benötigen Sie nicht tatsächlich ein echtes Modellobjekt, das von einem Webservice, einer Plist, einer SQLite-Datenbank oder CoreData initialisiert wurde?

Wie auch immer, hier kommt der Code (MPI ist für "My Project Initials", jeder verwendet diesen oder seinen Namen, wie es scheint):

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

Und 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
quelle
sieht gut aus, aber Sie weisen vollständige Wörterbücher zu und geben sie zurück, wenn Sie nur einen ihrer Werte benötigen. Effizienz VS Hübscher Code? hängt davon ab, was Sie wollen, und Sie werden damit einverstanden sein, wenn Sie sie nicht so oft in Ihrem Code verwenden wie in einer großen Schleife. Dies ist jedoch möglicherweise hilfreich bei "dynamischen" oder nicht fest codierten Aufzählungen, die beispielsweise von einem Server stammen
user2387149,
5

Eine andere Lösung:

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

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

In Ihrer Methode können Sie verwenden:

NSString *operationCheck = AMZCartServiceOperationValue(operation);
Kennymuse
quelle
4

Verbesserte @ yar1vn-Antwort durch Löschen der Zeichenfolgenabhängigkeit:

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

Wenn Sie also den Namen des Enum-Eintrags ändern, wird die entsprechende Zeichenfolge geändert. Nützlich für den Fall, dass Sie dem Benutzer diese Zeichenfolge nicht anzeigen möchten.

Bohdan Orlov
quelle
Können Sie "- define VariableName (arg) (@" "# arg) --- erklären und wahrscheinlich eine bessere Lösung geben?
xySVerma
Wenn Sie bei #defines # für eine Ersetzung verwenden, wird das Argument automatisch in doppelte Anführungszeichen gesetzt. Wenn in C zwei Zeichenfolgen im Code wie nebeneinander angezeigt werden "foo""bar", wird die Zeichenfolge "foobar"beim Kompilieren angezeigt. Also #define VariableName(arg) (@""#arg)wird sich erweitern VariableName(MyEnum)zu sein (@"""MyEnum"). Das führt zur Zeichenfolge @"MyEnum".
Chris Douglass
3

Gegeben eine Aufzählungsdefinition wie:

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

Wir können ein Makro definieren, um einen Aufzählungswert in die entsprechende Zeichenfolge zu konvertieren, wie unten gezeigt.

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

Die switchim Block verwendete Anweisung dient zur Typprüfung und zur Unterstützung der automatischen Vervollständigung in Xcode.

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

ylin0x81
quelle
2

Ich hatte einen großen Aufzählungstyp, den ich in eine NSDictionarySuche konvertieren wollte . Am Ende habe ich sedvom OSX-Terminal Folgendes verwendet:

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

Dies kann wie folgt gelesen werden: 'Erfassen Sie das erste Wort in der Zeile und geben Sie @ (Wort): @ "Wort" aus.'

Dieser reguläre Ausdruck konvertiert die Aufzählung in eine Header-Datei mit dem Namen 'ObservationType.h', die Folgendes enthält:

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

in so etwas wie:

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

@{ }Diese kann dann mithilfe der modernen Objective-C-Syntax (wie oben durch @ yar1vn erläutert) in eine Methode eingeschlossen werden , um eine NSDictionarySuche zu erstellen :

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

Die dispatch_onceKesselplatte soll lediglich sicherstellen, dass die statische Variable gewindesicher initialisiert wird.

Hinweis: Ich fand den sed regex-Ausdruck unter OSX seltsam - als ich versuchte, ihn +mit "einem oder mehreren" abzugleichen, funktionierte er nicht und musste ihn {1,}als Ersatz verwenden

Nick Ager
quelle
2

Ich verwende eine Variation der Antwort von Barry Walk, die in der Reihenfolge ihrer Wichtigkeit lautet:

  1. Ermöglicht dem Compiler, nach fehlenden case-Klauseln zu suchen (dies ist nicht möglich, wenn Sie eine Standardklausel haben).
  2. Verwendet einen typischen Objective-C-Namen (anstelle eines Java-ähnlichen Namens).
  3. Löst eine bestimmte Ausnahme aus.
  4. Ist kürzer.

Z.B:

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

@pixel hat hier die brillanteste Antwort hinzugefügt: https://stackoverflow.com/a/24255387/1364257 Bitte, stimmen Sie ihn ab!

Er benutzt das nette X-Makro aus den 1960ern. (Ich habe seinen Code für das moderne ObjC ein wenig geändert)

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

Das ist es. Sauber und ordentlich. Danke an @pixel! https://stackoverflow.com/users/21804/pixel

voiger
quelle
@AlexandreG bieten Ihre Lösung, Mann. Es ist leicht, jemanden zu karpfen. Diese Lösung hat ihre offensichtlichen Vor- und Nachteile. Machen Sie die Welt mit Ihrer Lösung besser.
Voiger
2

Ich habe hier mehrere Ansätze kombiniert. Ich mag die Idee des Präprozessors und der indizierten Liste.

Es gibt keine zusätzliche dynamische Zuordnung, und aufgrund des Inlinings kann der Compiler möglicherweise die Suche optimieren.

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

quelle
1

Zunächst in Bezug auf FormatType.JSON: JSON ist kein Mitglied von FormatType, sondern ein möglicher Wert des Typs. FormatType ist nicht einmal ein zusammengesetzter Typ - es ist ein Skalar.

Zweitens besteht die einzige Möglichkeit darin, eine Zuordnungstabelle zu erstellen. Der üblichere Weg, dies in Objective-C zu tun, besteht darin, eine Reihe von Konstanten zu erstellen, die sich auf Ihre "Symbole" beziehenNSString *FormatTypeJSON = @"JSON" und so weiter.

Futter
quelle
1

Im Folgenden finden Sie eine Lösung, bei der zum Hinzufügen einer neuen Aufzählung nur eine einzeilige Bearbeitung erforderlich ist. Dies ähnelt dem Hinzufügen einer einzelnen Zeile in einer Aufzählungsliste {}.

//------------------------------------------------------------------------------
// 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
quelle
Diese Technik wird als X-Macro bezeichnet, falls jemand darüber lesen möchte. Dies liegt daran, dass das Makro FOR_EACH_GENDER () traditionell immer nur X () genannt wurde. Möglicherweise möchten Sie #undef FOR_EACH_GENDER ausführen, bevor Sie es mit einer neuen Bedeutung neu definieren.
uliwitness
1

Jede Antwort hier sagt im Grunde dasselbe, erstellt eine reguläre Aufzählung und verwendet dann einen benutzerdefinierten Getter, um zwischen Zeichenfolgen zu wechseln.

Ich verwende eine viel einfachere Lösung, die schneller, kürzer und sauberer ist - mit Makros!


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

Dann können Sie einfach mit der Eingabe beginnen kNam... Eingabe und die automatische Vervollständigung zeigt die gewünschten Listen an!

Wenn Sie die Logik für alle Namen gleichzeitig verarbeiten möchten, können Sie das Literal-Array einfach wie folgt schnell auflisten:

for (NSString *kName in kNames_allNames) {}

Schließlich sorgt das NSString-Casting in den Makros für ein ähnliches Verhalten wie typedef!


Genießen!

Albert Renshaw
quelle
0

Viele Antworten sind alle ziemlich gut.

Wenn Sie nach einer generischen Objective C-Lösung suchen, die einige Makros verwendet ...

Das Hauptmerkmal ist, dass die Aufzählung als Index für ein statisches Array von NSString-Konstanten verwendet wird. Das Array selbst ist in eine Funktion eingebunden, um es der in den Apple-APIs vorherrschenden Suite von NSStringFromXXX-Funktionen ähnlicher zu machen.

Sie müssen #import "NSStringFromEnum.h"hier http://pastebin.com/u83RR3Vk finden

[EDIT] muss #import "SW+Variadic.h"auch hier gefunden werden http://pastebin.com/UEqTzYLf

Beispiel 1: Definieren Sie eine NEUE Aufzählung typedef mit Zeichenfolgenkonvertern vollständig.

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)

benutzen :

NSString *NSStringFromEnumDispatch_chain_cmd(enum Dispatch_chain_cmd value);

NSStringFromEnumDispatch_chain_cmd(chain_for_c) kehrt zurück @"chain_for_c"

  enum Dispatch_chain_cmd enumDispatch_chain_cmdFromNSString(NSString *value);

enumDispatch_chain_cmdFromNSString(@"chain_previous") kehrt zurück chain_previous

Beispiel 2: Das Bereitstellen von Konvertierungsroutinen für eine vorhandene Aufzählung zeigt auch die Verwendung einer Einstellungszeichenfolge und das Umbenennen des in den Funktionen verwendeten Typnamens.

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)
nicht synchronisiert
quelle
0

Hier funktioniert -> 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
quelle
Da eine doppelte Antwort nicht erlaubt ist, ist hier die vollständige Lösung github.com/ndpiparava/ObjcEnumString
Nitin
-2

Abhängig von Ihren Anforderungen können Sie alternativ Compiler-Anweisungen verwenden, um das gesuchte Verhalten zu simulieren.

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

Denken Sie nur an die üblichen Mängel des Compilers (nicht typsicher, direktes Kopieren und Einfügen vergrößert die Quelldatei)

Alex Gosselin
quelle
8
Ich denke nicht, dass das funktionieren wird. Überall dort, wo das #definesichtbar ist, können Sie den tatsächlichen Aufzählungswert nicht verwenden (dh er JSONwird @"JSON"durch den Präprozessor ersetzt und führt zu einem Compilerfehler bei der Zuweisung zu einem FormatType.
Barry Wark