web-dev-qa-db-fra.com

valeurs d'énumération: NSInteger ou int?

version tl; dr

Comment les types de données des constantes d'une énumération sont-ils garantis comme étant NSUInteger au lieu de entier non signé lors de la déclaration d'une énumération:

enum {
    NSNullCellType = 0,
    NSTextCellType = 1,
    NSImageCellType = 2
};
typedef NSUInteger NSCellType;

Le typedef de NSUInteger ne semble en aucune façon lié à la déclaration d'énumération.

Version complète

Je lisais à travers Apple Guide de transition 64 bits pour Cocoa pour obtenir des conseils sur les valeurs d'énumération et je suis reparti avec une question . Voici une (longue) citation de la section Constantes d'énumération , en soulignant la mienne:

Un problème avec les constantes d'énumération (enum) est que leurs types de données sont souvent indéterminés. En d'autres termes, les constantes enum ne sont pas prévisibles non signées int. Avec les énumérations construites de manière conventionnelle, le compilateur définit en fait le type sous-jacent en fonction de ce qu'il trouve. Le type sous-jacent peut être (signé) entier ou même long. Prenons l'exemple suivant:

type enum {
    MyFlagError = -1,
    MyFlagLow = 0,
    MyFlagMiddle = 1,
    MyFlagHigh = 2
} MyFlagType;

Le compilateur examine cette déclaration et, en trouvant une valeur négative affectée à l'une des constantes membres, déclare le type sous-jacent de l'énumération int. Si la plage de valeurs pour les membres ne correspond pas à un entier ou à un entier non signé, le type de base devient alors 64 bits (long). Le type de base des quantités définies comme énumérations peut ainsi changer silencieusement de taille pour s'accorder avec les valeurs de l'énumération. Cela peut se produire que vous compiliez 32 bits ou 64 bits. Il va sans dire que cette situation présente des obstacles à la compatibilité binaire.

Pour remédier à ce problème, Apple a décidé d'être plus explicite sur le type d'énumération dans l'API Cocoa. Au lieu de déclarer des arguments en termes d'énumération, les fichiers d'en-tête déclarent désormais séparément un type pour l'énumération dont la taille peut être spécifiée. Les membres de l'énumération et leurs valeurs sont déclarés et affectés comme précédemment. Par exemple, au lieu de cela:

typedef enum {
    NSNullCellType = 0,
    NSTextCellType = 1,
    NSImageCellType = 2
} NSCellType;

il y a maintenant ceci:

enum {
    NSNullCellType = 0,
    NSTextCellType = 1,
    NSImageCellType = 2
};
typedef NSUInteger NSCellType;

Le type d'énumération est défini en termes de NSInteger ou NSUInteger pour rendre le type d'énumération de base compatible 64 bits sur les architectures 64 bits.

Ma question est la suivante: étant donné que le typedef ne semble pas être lié explicitement à la déclaration enum, comment sait-on si leurs types de données ne sont pas signés int ou NSUInteger?

39
Michael Fey

Il y a maintenant NS_ENUM à partir de Xcode 4.5:

typedef NS_ENUM(NSUInteger, NSCellType) {
    NSNullCellType = 0,
    NSTextCellType = 1,
    NSImageCellType = 2
};

Et vous pouvez envisager NS_OPTIONS si vous travaillez avec des drapeaux binaires:

typedef NS_OPTIONS(NSUInteger, MyCellFlag) {
    MyTextCellFlag = 1 << 0,
    MyImageCellFlag = 1 << 1,
};
54
Cœur

Je lance un test sur le simulateur de sorte que l'intention du test est de vérifier la taille des différents types d'entiers. Pour cela, le résultat de sizeof a été imprimé dans la console. Je teste donc ces valeurs de enum:

      
typedef enum {
    TLEnumCero = 0,
    TLEnumOne = 1,
    TLEnumTwo = 2
} TLEnum;

typedef enum {
    TLEnumNegativeMinusOne = -1,
    TLEnumNegativeCero = 0,
    TLEnumNegativeOne = 1,
    TLEnumNegativeTwo = 2
} TLEnumNegative;

typedef NS_ENUM(NSUInteger, TLUIntegerEnum) {
    TLUIntegerEnumZero = 0,
    TLUIntegerEnumOne = 1,
    TLUIntegerEnumTwo = 2
};

typedef NS_ENUM(NSInteger, TLIntegerEnum) {
    TLIntegerEnumMinusOne = -1,
    TLIntegerEnumZero = 0,
    TLIntegerEnumOne = 1,
    TLIntegerEnumTwo = 2
};

Code de test:


    NSLog(@"sizeof enum: %ld", sizeof(TLEnum));
    NSLog(@"sizeof enum negative: %ld", sizeof(TLEnumNegative));
    NSLog(@"sizeof enum NSUInteger: %ld", sizeof(TLUIntegerEnum));
    NSLog(@"sizeof enum NSInteger: %ld", sizeof(TLIntegerEnum));

Résultat pour iPhone Retina (4 pouces) Simulator:


sizeof enum: 4
sizeof enum negative: 4
sizeof enum NSUInteger: 4
sizeof enum NSInteger: 4

Résultat pour iPhone Retina (4 pouces 64 bits) Simulator:


sizeof enum: 4
sizeof enum negative: 4
sizeof enum NSUInteger: 8
sizeof enum NSInteger: 8

Conclusion

Un enum générique peut être un int ou unsigned int types de 4 octets pour 32 ou 64 bits. Comme nous le savons déjà, NSUInteger et NSInteger sont 4 octets pour 32 bits et 8 octets dans un compilateur 64 bits pour iOS.

17
javienegas

Ce sont deux déclarations distinctes. Le typedef garantit que, lorsque vous utilisez ce type, vous obtenez toujours un NSUInteger.

Le problème avec une énumération n'est pas qu'elle n'est pas assez grande pour contenir la valeur. En fait, la seule garantie que vous obtenez pour une énumération est que sizeof (enum Foo) est suffisamment grand pour contenir toutes les valeurs que vous avez actuellement définies dans cette énumération. Mais sa taille peut changer si vous ajoutez une autre constante. C'est pourquoi Apple faites le typedef séparé, pour maintenir la stabilité binaire de l'API.

7
uliwitness

Les types de données des constantes de l'énumération ne sont pas garantis être NSUInteger, mais ils sont garantis pour être convertis en NSUInteger chaque fois que vous les utilisez via NSCellType.

En d'autres termes, la déclaration décrète que, bien que les valeurs de l'énumération correspondent actuellement à un unsigned int, le stockage qui leur est réservé lors de l'accès via NSCellType doit être un NSUInteger.

2
hatfinch