Konwertuj zmienną typedef-c na jej odpowiednik w postaci łańcucha


141

Zakładając, że mam deklarację typedef w swoim pliku .h jako taką:

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

Chciałbym zbudować funkcję, która konwertuje wartość liczbową typu typedef na ciąg. Na przykład, jeśli wiadomość [self toString:JSON]została wysłana; zwróci „JSON”.

Funkcja wyglądałaby mniej więcej tak:

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

Nawiasem mówiąc, jeśli spróbuję tej składni

[self toString:FormatType.JSON];

aby przekazać wartość typedef do metody, pojawia się błąd. czego mi brakuje?


Zobacz moją odpowiedź na stackoverflow.com/questions/6331762/enum-values-to-nsstring-ios, aby uzyskać bardziej przejrzyste rozwiązanie problemu.
BooTooMany

4
Może powinniśmy przytulić język Swift na enum.
Itachi

@craig: tutaj jest rozwiązanie github.com/ndpiparava/ObjcEnumString
Nitin

Aby uzyskać rozwiązanie bez prostego użycia niestandardowego narzędzia pobierającego dla wyliczenia, które konwertuje się na ciąg, sprawdź tablicę makr rzutowanych na ciąg: stackoverflow.com/a/53755377/2057171
Albert Renshaw,

Odpowiedzi:


135

To jest naprawdę pytanie C, nie specyficzne dla Objective-C (który jest nadzbiorem języka C). Wyliczenia w C są przedstawiane jako liczby całkowite. Musisz więc napisać funkcję, która zwraca ciąg znaków o wartości wyliczenia. Istnieje wiele sposobów, aby to zrobić. Tablica ciągów, taka że wartość wyliczenia może być używana jako indeks tablicy lub struktury mapy (np. NSDictionary), Która mapuje wartość wyliczenia na ciąg znaków, ale uważam, że te podejścia nie są tak jasne, jak funkcja, która sprawia, że ​​konwersja jest jawna (i podejście tablicowe, chociaż klasyczny Csposób jest niebezpieczny, jeśli wartości wyliczenia nie są ciągłe od 0). Coś takiego zadziałałoby:

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

Twoje pokrewne pytanie dotyczące prawidłowej składni wartości wyliczenia dotyczy tego, że używasz tylko wartości (np. JSON), A nie FormatType.JSONsytax. FormatTypeJest to rodzaj i wartości enum (np JSON, XMLetc.) są wartościami, które można przypisać do danego typu.


127

Nie możesz tego łatwo zrobić. W C i Objective-C wyliczenia są w rzeczywistości gloryfikowanymi stałymi całkowitymi. Będziesz musiał samodzielnie wygenerować tabelę nazw (lub z pewnym nadużyciem preprocesora). Na przykład:

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

Niebezpieczeństwo tego podejścia polega na tym, że jeśli kiedykolwiek zmienisz wyliczenie, musisz pamiętać o zmianie tablicy nazw. Możesz rozwiązać ten problem, używając pewnego nadużycia preprocesora, ale jest to trudne i brzydkie.

Zauważ również, że przy założeniu, że masz prawidłową stałą wyliczenia. Jeśli wartość całkowitą z niezaufanych źródeł, dodatkowo trzeba zrobić czek, że stała jest ważny, na przykład przez wprowadzenie „przeszłość max” wartość w swojej wyliczenia, lub sprawdzając, czy jest to mniej niż długość tablicy, sizeof(FormatType_toString) / sizeof(FormatType_toString[0]).


37
możesz zainicjować tablice z jawnymi indeksami, np. string[] = { [XML] = "XML" }aby upewnić się, że ciąg pasuje do wyliczeń
Christoph

@Christoph: Tak, to jest funkcja C99 zwana wyznaczonymi inicjalizatorami . Jest to dobre w użyciu w Objective-C (opartym na C99), ale w przypadku ogólnego kodu C89 nie można ich używać.
Adam Rosenfield,

Czy jest jakiś sposób, aby pójść w drugą stronę? Na przykład, odzyskać wyliczenie otrzymane jako ciąg znaków?
Jameo,

1
@Jameo: Tak, ale nie jest to tak proste, jak wyszukiwanie w tablicy. Będziesz musiał albo iterować przez FormatType_toString[]tablicę i wywołać -isEqualToString:każdy element, aby znaleźć dopasowanie, albo użyć typu danych mapowania, takiego jak NSDictionaryutrzymywanie odwrotnej mapy wyszukiwania.
Adam Rosenfield

1
Sztuczka Maxa O polega na zapomnieniu o dodaniu wpisów do FormatType_toStringtablicy.
AechoLiu

50

Moje rozwiązanie:

edycja: na końcu dodałem jeszcze lepsze rozwiązanie, używając nowoczesnego Obj-C

1.
Umieść nazwy jako klucze w tablicy.
Upewnij się, że indeksy są odpowiednimi wyliczeniami i we właściwej kolejności (w przeciwnym razie wyjątek).
uwaga: nazwy to właściwość syntetyzowana jako * _names *;

kod nie został sprawdzony pod kątem kompilacji, ale użyłem tej samej techniki w mojej aplikacji.

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.
Używając nowoczesnego Obj-C możemy użyć słownika do powiązania opisów z kluczami w wyliczeniu.
Zamówienie NIE MA znaczenia .

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


Użycie (w metodzie instancji klasy):

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



12
Pamiętaj, że za każdym razem, gdy dzwonisz +[typeDisplayNames], ponownie tworzysz słownik. Jest to w porządku, jeśli dzwoni się tylko kilka razy, ale jeśli dzwoni się wiele razy, będzie to bardzo kosztowne. Lepszym rozwiązaniem może być uczynienie ze słownika singletonu, więc jest on tworzony tylko raz iw innym przypadku pozostaje w pamięci. Klasyczna pamięć a zagadka procesora.
Joel Fischer,

Lub zmień ją na zmienną statyczną, np. static NSDictionary *dict = nil; if(!dict) dict = @{@(UserTypeParent): @"Parent"}; return dict;Komentarze nie pozwolą ci na łamanie linii, przepraszam za to.
natanavra

29

Łącząc odpowiedź @AdamRosenfield, komentarz @Christoph i inną sztuczkę do obsługi zwykłych wyliczeń C, które sugeruję:

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

W najgorszym przypadku - na przykład, jeśli zmienisz wyliczenie, ale zapomnisz zmienić tablicę nazw - zwróci to nil dla tego klucza.


12

zdefiniuj typedef enum w nagłówku klasy:

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

napisz taką metodę w klasie:

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

mają ciągi znaków w pliku Localizable.strings :

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

11

Użyłbym tokena ciągu # kompilatora (wraz z makrami, aby uczynić go bardziej kompaktowym):

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

To działało świetnie w C99 - jestem nowy w C i stwierdziłem, że to najczystszy sposób na wykonanie zadanego pytania. Dodałem również domyślnie w mojej implementacji dla pozycji, które mogły nie zostać zdefiniowane. Bardzo czysta metoda. Dzięki za wyniki. Bardzo sprytne użycie makra.
TravisWhidden

8

Podoba mi się #definesposób robienia tego:

// Umieść to w swoim pliku .h, poza blokiem @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];
}

źródło (źródło nie jest już dostępne)


@ Daij-Djan a co z powrotem, niljeśli array.count <= enumValue?
anneblue

@anneblue, który wyłapałby błąd ... nadal byłby kruchy, ponieważ jeśli dodasz wartość wyliczenia LUB zmienisz wartość całkowitą wartości wyliczenia, to pójdzie nie tak. Przyjęta odpowiedź byłaby dobra
Daij-Djan

@codercat :( przepraszam - nie wiem, co się stało z tą witryną. Nie w drodze
powrotnej,

Mam małe pytanie do powyższej odpowiedzi. Jak przekonwertować element string na kImageType. Muszę wywołać metodę imageTypeEnumToString, przekazując ciąg znaków. Czy możesz mi pomóc rozwiązać mój problem.
Ganesh

1
Najbardziej podoba mi się ta odpowiedź, ponieważ definicje ciągów znajdują się tuż obok wyliczeń. Najmniejsza szansa na przeoczenie wartości. A @Ganesh, aby przekonwertować z wartości surowej, może to zrobić: return (kImageType) [imageTypeArray indexOfObject: rawValue];
Harris

8

Zrobiłem coś w rodzaju mieszanki wszystkich rozwiązań znalezionych na tej stronie, aby stworzyć moje, jest to rodzaj zorientowanego obiektowo rozszerzenia wyliczenia lub coś w tym rodzaju.

W rzeczywistości, jeśli potrzebujesz czegoś więcej niż tylko stałych (tj. Liczb całkowitych), prawdopodobnie potrzebujesz obiektu modelu (wszyscy mówimy o MVC, prawda?)

Po prostu zadaj sobie pytanie przed użyciem tego, czy mam rację, czy w rzeczywistości nie potrzebujesz prawdziwego obiektu modelu, zainicjowanego z usługi sieciowej, plist, bazy danych SQLite lub CoreData?

W każdym razie tutaj jest kod (MPI oznacza „Moje inicjały projektu”, wszyscy używają tego lub ich nazwy):

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

Oraz 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

wygląda ładnie, ale przydzielasz i zwracasz pełne słowniki, gdy potrzebujesz tylko jednej z jego wartości. Wydajność czy ładny kod? zależy od tego, czego chcesz i będzie dobrze z tym, jeśli nie używasz ich tak często w swoim kodzie, jak w ogromnej pętli. Ale może to być przydatne w przypadku „dynamicznych” lub nie zakodowanych na stałe
wyliczeń

5

Inne rozwiązanie:

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

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

W swojej metodzie możesz użyć:

NSString *operationCheck = AMZCartServiceOperationValue(operation);

4

Ulepszona odpowiedź @ yar1vn przez usunięcie zależności od ciągu:

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

Tak więc, kiedy zmienisz nazwę wpisu wyliczenia, odpowiedni ciąg zostanie zmieniony. Przydatne w przypadku, gdy nie zamierzasz wyświetlać tego ciągu użytkownikowi.


Czy możesz wyjaśnić "- zdefiniuj VariableName (arg) (@" "# arg) --- i prawdopodobnie podaj lepsze rozwiązanie?
xySVerma

W przypadku #defines, gdy używasz znaku # do podstawienia, argument jest automatycznie zawijany w podwójne cudzysłowy. W języku C, gdy dwa ciągi pojawiają się obok siebie w kodzie takim jak "foo""bar", "foobar"po kompilacji powstaje ciąg . Tak, #define VariableName(arg) (@""#arg)będzie rozwijać VariableName(MyEnum)się (@"""MyEnum"). Spowoduje to powstanie ciągu @"MyEnum".
Chris Douglass

3

Biorąc pod uwagę definicję wyliczenia, taką jak:

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

Możemy zdefiniować makro, aby przekonwertować wartość wyliczenia na odpowiadający jej ciąg, jak pokazano poniżej.

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

switchWykorzystywane w bloku stwierdzenie jest dla typu kontroli, a także uzyskać autouzupełniania wsparcie w Xcode.

wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj


2

Miałem duży wyliczony typ i chciałem przekształcić go w NSDictionarywyszukiwanie. Skończyło się na używaniu sedz terminala OSX jako:

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

które można odczytać jako: 'przechwyć pierwsze słowo w wierszu i wypisz @ (słowo): @ "słowo",'

To wyrażenie regularne konwertuje wyliczenie w pliku nagłówkowym o nazwie „ObservationType.h”, który zawiera:

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

na coś takiego:

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

które można następnie @{ }opakować w metodę używającą nowoczesnej składni obiektywnej-c (jak wyjaśniono powyżej w @ yar1vn), aby utworzyć NSDictionaryodnośnik:

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

Plik dispatch_once kotłowa służy tylko do zapewnienia inicjalizacji zmiennej statycznej w sposób bezpieczny dla wątków.

Uwaga: stwierdziłem, że wyrażenie wyrażenia regularnego sed na OSX jest dziwne - kiedy próbowałem użyć +dopasowania `` jednego lub więcej '', nie działało i musiałem uciekać się do użycia {1,}jako zamiennika


2

Używam wariacji na temat odpowiedzi Barry'ego Walk'a, w kolejności od najważniejszej:

  1. Umożliwia kompilatorowi sprawdzenie brakujących klauzul wielkości liter (nie może, jeśli masz klauzulę domyślną).
  2. Używa typowej nazwy Objective-C (zamiast nazwy podobnej do Java).
  3. Podnosi określony wyjątek.
  4. Jest krótszy.

NA PRZYKŁAD:

- (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 dodał tutaj najbardziej genialną odpowiedź: https://stackoverflow.com/a/24255387/1364257 Proszę, zagłosuj na niego!

Używa zgrabnego makra X z lat 60-tych. (Zmieniłem nieco jego kod dla nowoczesnego 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];
}

Otóż ​​to. Czysto i schludnie. Dzięki @pixel! https://stackoverflow.com/users/21804/pixel


@AlexandreG zapewnij swoje rozwiązanie, człowieku. Łatwo na kogoś złapać karpia. To rozwiązanie ma zarówno oczywiste zalety, jak i wady. Ulepsz świat dzięki swojemu rozwiązaniu.
voiger,

2

Połączyłem tutaj kilka podejść. Podoba mi się pomysł preprocesora i zindeksowanej listy.

Nie ma dodatkowej alokacji dynamicznej, a ze względu na wbudowanie kompilator może być w stanie zoptymalizować wyszukiwanie.

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

Po pierwsze, w odniesieniu do FormatType.JSON: JSON nie jest członkiem FormatType, jest to możliwa wartość typu. FormatType nie jest nawet typem złożonym - to skalar.

Po drugie, jedynym sposobem na to jest utworzenie tabeli mapowania. Bardziej powszechnym sposobem na zrobienie tego w Objective-C jest utworzenie serii stałych odnoszących się do twoich „symboli”, tak abyś miał NSString *FormatTypeJSON = @"JSON"i tak dalej.


1

Poniżej przedstawiono rozwiązanie polegające na tym, że dodanie nowego wyliczenia wymaga tylko edycji w jednym wierszu, podobnej pracy do dodawania pojedynczego wiersza na liście wyliczeń {}.

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

Ta technika nazywa się X-Macro, na wypadek gdyby ktoś chciał o tym poczytać. Wynika to z faktu, że tradycyjnie makro FOR_EACH_GENDER () było zawsze nazywane po prostu X (). Jedną rzeczą, którą możesz chcieć zrobić, jest #undef FOR_EACH_GENDER, zanim przedefiniujesz go w nowym znaczeniu.
uliwitness

1

Każda odpowiedź tutaj w zasadzie mówi to samo, utwórz zwykłe wyliczenie, a następnie użyj niestandardowego narzędzia pobierającego, aby przełączać się między ciągami.

Używam znacznie prostszego rozwiązania, które jest szybsze, krótsze i czystsze - używając makr!


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

Następnie możesz po prostu zacząć pisać kNam... a autouzupełnianie wyświetli żądane listy!

Dodatkowo, jeśli chcesz obsłużyć logikę dla wszystkich nazw naraz, możesz po prostu szybko wyliczyć tablicę literałów w kolejności, w następujący sposób:

for (NSString *kName in kNames_allNames) {}

Na koniec rzutowanie NSString w makrach zapewnia zachowanie podobne do typedef!


Cieszyć się!


0

Wiele odpowiedzi jest całkiem dobrych.

Jeśli szukasz ogólnego rozwiązania Objective C, które wykorzystuje niektóre makra ...

Kluczową cechą jest to, że używa wyliczenia jako indeksu do statycznej tablicy stałych NSString. sama tablica jest opakowana w funkcję, aby bardziej przypominała zestaw funkcji NSStringFromXXX rozpowszechnionych w interfejsach API Apple.

musisz #import "NSStringFromEnum.h"znaleźć tutaj http://pastebin.com/u83RR3Vk

[EDYCJA] również wymaga #import "SW+Variadic.h"znalezienia tutaj http://pastebin.com/UEqTzYLf

Przykład 1: całkowicie zdefiniuj NOWY typ wyliczenia z konwerterami ciągów.

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

w myfile.m:


 #import "myfile.h"

 implementation_NSString_Enum_Converters(Dispatch_chain_cmd)

używać :

NSString *NSStringFromEnumDispatch_chain_cmd(enum Dispatch_chain_cmd value);

NSStringFromEnumDispatch_chain_cmd(chain_for_c) zwroty @"chain_for_c"

  enum Dispatch_chain_cmd enumDispatch_chain_cmdFromNSString(NSString *value);

enumDispatch_chain_cmdFromNSString(@"chain_previous") zwroty chain_previous

Przykład 2: dostarczanie procedur konwersji dla istniejącego wyliczenia demonstruje również użycie ciągu ustawień i zmianę nazwy nazwy typu używanej w funkcjach.

w myfile.h


 #import "NSStringFromEnum.h"


 #define CAEdgeAntialiasingMask_SETTINGS_PARAMS CAEdgeAntialiasingMask,mask,EdgeMask,edgeMask

 interface_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS)

w 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

Tutaj działa -> 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;
}

ponieważ podwójna
Nitin

-2

W zależności od potrzeb możesz alternatywnie użyć dyrektyw kompilatora, aby zasymulować zachowanie, którego szukasz.

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

Pamiętaj tylko o typowych wadach kompilatora (nie wpisuj bezpiecznego, bezpośrednie kopiowanie i wklejanie zwiększa rozmiar pliku źródłowego)


8
Myślę, że to nie zadziała; wszędzie tam, gdzie #definejest widoczne, nie będzie można użyć rzeczywistej wartości wyliczenia (tj. JSONzostanie zastąpiona @"JSON"przez preprocesor i spowoduje błąd kompilatora podczas przypisywania do a FormatType.
Barry Wark
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.