146 votes

Convertir un typage objective-c en son équivalent chaîne de caractères

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

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

Je voudrais construire une fonction qui convertit la valeur numérique du typedef en une chaîne de caractères. Par exemple, si le message [self toString:JSON] a été envoyé ; il renvoie "JSON".

La fonction ressemblerait à ceci :

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

Par ailleurs, si j'essaie cette syntaxe

[self toString:FormatType.JSON];

pour passer la valeur du typedef à la méthode, j'obtiens une erreur. Que me manque-t-il ?

0 votes

Voir ma réponse à stackoverflow.com/questions/6331762/enum-values-to-nsstring-ios pour une solution plus propre à ce problème.

4 votes

On devrait peut-être faire un câlin à Swift langue sur l'enum.

0 votes

@craig : voici la solution github.com/ndpiparava/ObjcEnumString

136voto

Barry Wark Points 73462

Il s'agit en fait d'une question concernant le langage C, qui n'est pas spécifique à l'Objective-C (qui est un surensemble du langage C). En C, les enums sont représentés par des entiers. Vous devez donc écrire une fonction qui renvoie une chaîne de caractères en fonction de la valeur d'un enum. Il y a plusieurs façons de le faire. Un tableau de chaînes de caractères tel que la valeur de l'énumération peut être utilisée comme index dans le tableau, ou une structure de carte (par exemple, une structure NSDictionary ) qui font correspondre une valeur d'énumération à une chaîne de caractères fonctionnent, mais je trouve que ces approches ne sont pas aussi claires qu'une fonction qui rend la conversion explicite (et l'approche par tableau, bien que le classique C est dangereuse si les valeurs de votre enum ne sont pas contingentes à 0). Quelque chose comme ceci fonctionnerait :

- (NSString*)formatTypeToString:(FormatType)formatType {
    NSString *result = nil;

    switch(formatType) {
        case JSON:
            result = @"JSON";
            break;
        case XML:
            result = @"XML";
            break;
        case Atom:
            result = @"Atom";
            break;
        case RSS:
            result = @"RSS";
            break;
        default:
            [NSException raise:NSGenericException format:@"Unexpected FormatType."];
    }

    return result;
}

Votre question connexe sur la syntaxe correcte d'une valeur d'énumération est que vous utilisez uniquement la valeur (par ex. JSON ), et non le FormatType.JSON sytaxe. FormatType est un type et les valeurs de l'enum (par ex. JSON , XML etc.) sont des valeurs que vous pouvez attribuer à ce type.

129voto

Adam Rosenfield Points 176408

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

// In a header file
typedef enum FormatType {
    JSON,
    XML,
    Atom,
    RSS
} FormatType;

extern NSString * const FormatType_toString[];

// In a source file
// initialize arrays with explicit indices to make sure 
// the string match the enums properly
NSString * const FormatType_toString[] = {
    [JSON] = @"JSON",
    [XML] = @"XML",
    [Atom] = @"Atom",
    [RSS] = @"RSS"
};
...
// To convert enum to string:
NSString *str = FormatType_toString[theEnumValue];

Le danger de cette approche est que si vous modifiez l'énumération, vous devez vous souvenir de modifier le tableau des noms. Vous pouvez résoudre ce problème en abusant du préprocesseur, mais c'est délicat et laid.

Notez également que cela suppose que vous avez une constante enum valide. Si vous avez une valeur entière provenant d'une source non fiable, vous devez en plus vérifier que votre constante est valide, par exemple en incluant une valeur "past max" dans votre enum, ou en vérifiant si elle est inférieure à la longueur du tableau, sizeof(FormatType_toString) / sizeof(FormatType_toString[0]) .

37 votes

Vous pouvez initialiser des tableaux avec des indices explicites, par exemple string[] = { [XML] = "XML" } pour s'assurer que la chaîne correspond aux enums correctement

0 votes

@Christoph : Oui, c'est une fonctionnalité de C99 appelée initialisateurs désignés . On peut les utiliser en Objective-C (qui est basé sur C99), mais pour le code générique C89, on ne peut pas les utiliser.

0 votes

Y a-t-il un moyen d'aller dans l'autre sens ? Par exemple, récupérer l'enum à partir d'une chaîne de caractères ?

50voto

yar1vn Points 1472

Ma solution :

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

  1. Mettre les noms comme clés dans un tableau.
    Assurez-vous que les index sont les enums appropriés, et dans le bon ordre (sinon exception).
    note : noms est une propriété synthétisée comme *_names* ;

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

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

+ (NSArray *)names
{
    static NSMutableArray * _names = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _names = [NSMutableArray arrayWithCapacity:4];
        [_names insertObject:@"JSON" atIndex:JSON];
        [_names insertObject:@"XML" atIndex:XML];
        [_names insertObject:@"Atom" atIndex:Atom];
        [_names insertObject:@"RSS" atIndex:RSS];
    });

    return _names;
}

+ (NSString *)nameForType:(FormatType)type
{
    return [[self names] objectAtIndex:type];
}

//

  1. En utilisant Obj-C moderne, nous pouvons utiliser un dictionnaire pour lier les descriptions aux clés de l'enum.
    L'ordre n'a pas d'importance .

    typedef NS_ENUM(NSUInteger, UserType) { UserTypeParent = 0, UserTypeStudent = 1, UserTypeTutor = 2, UserTypeUnknown = NSUIntegerMax };

    @property (nonatomic) UserType type;

    • (NSDictionary *)typeDisplayNames { return @{@(UserTypeParent) : @"Parent", @(UserTypeStudent) : @"Student", @(UserTypeTutor) : @"Tutor", @(UserTypeUnknown) : @"Unknown"}; }

    • (NSString *)typeDisplayName { return [[self class] typeDisplayNames][@(self.type)]; }

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

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

12 votes

Sachez que chaque fois que vous appelez +[typeDisplayNames] vous recréez le dictionnaire. Cela ne pose pas de problème s'il n'est appelé que quelques fois, mais s'il est appelé plusieurs fois, cela devient très coûteux. Une meilleure solution pourrait être de faire du dictionnaire un singleton, de sorte qu'il ne soit créé qu'une seule fois et reste en mémoire sinon. Une énigme classique entre la mémoire et le processeur.

0 votes

Ou bien changez-la pour qu'elle soit une variable statique, par ex. static NSDictionary *dict = nil; if(!dict) dict = @{@(UserTypeParent): @"Parent"}; return dict; Les commentaires ne permettent pas le saut de ligne, désolé pour cela.

29voto

Max O Points 473

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

// In a header file
typedef enum {
  JSON = 0,         // explicitly indicate starting index
  XML,
  Atom,
  RSS,

  FormatTypeCount,  // keep track of the enum size automatically
} FormatType;
extern NSString *const FormatTypeName[FormatTypeCount];

// In a source file
NSString *const FormatTypeName[FormatTypeCount] = {
  [JSON] = @"JSON",
  [XML] = @"XML",
  [Atom] = @"Atom",
  [RSS] = @"RSS",
};

// Usage
NSLog(@"%@", FormatTypeName[XML]);

Dans le pire des cas - comme si vous modifiez l'enum mais oubliez de modifier le tableau des noms - il retournera nil pour cette clé.

12voto

kitschmaster Points 798

Définir typedef enum dans la classe header :

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

écrire une méthode comme celle-ci dans la classe :

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

ont les cordes à l'intérieur Localizable.strings fichier :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X