objective-c cocoa enums typedef nsinteger

objective c - valores de enum: NSInteger o int?



objective-c cocoa (4)

Versión de tl; dr

¿Cómo se garantiza que los tipos de datos de las constantes de una enumeración serán NSUInteger en lugar de unsigned int cuando se declara una enumeración así:

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

El typedef a NSUInteger no parece estar vinculado a la declaración enum de ninguna manera.

Versión completa

Estaba leyendo la Guía de transición de 64 bits de Apple para Cocoa para obtener orientación sobre los valores enum y me salió una pregunta. Aquí hay una cita (larga) de la sección Constantes de enumeración , énfasis mío:

Un problema con las constantes de enumeración (enum) es que sus tipos de datos son frecuentemente indeterminados. En otras palabras, las constantes enum no son predeciblemente unsigned int. Con enumeraciones construidas de forma convencional, el compilador realmente establece el tipo subyacente según lo que encuentre. El tipo subyacente puede ser (firmado) int o incluso largo. Toma el siguiente ejemplo:

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

El compilador observa esta declaración y, al encontrar un valor negativo asignado a una de las constantes miembro, declara el tipo subyacente de la enumeración int. Si el rango de valores para los miembros no cabe en una int o unsigned int, entonces el tipo de base se vuelve silenciosamente de 64 bits (long). El tipo base de cantidades definidas como enumeraciones puede cambiar silenciosamente el tamaño para que coincida con los valores en la enumeración. Esto puede suceder ya sea que esté compilando 32 bits o 64 bits. Huelga decir que esta situación presenta obstáculos para la compatibilidad binaria.

Como remedio para este problema, Apple ha decidido ser más explícito sobre el tipo de enumeración en la API de Cocoa. En lugar de declarar argumentos en términos de la enumeración, los archivos de encabezado ahora declaran por separado un tipo para la enumeración cuyo tamaño se puede especificar. Los miembros de la enumeración y sus valores se declaran y asignan como antes. Por ejemplo, en lugar de esto:

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

ahora hay esto:

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

El tipo de enumeración se define en términos de NSInteger o NSUInteger para hacer que el tipo de enumeración base sea de 64 bits en arquitecturas de 64 bits.

Mi pregunta es la siguiente: dado que typedef no parece estar vinculado explícitamente a la declaración enum, ¿cómo se sabe si sus tipos de datos no tienen firma int o NSUInteger?


Ahora hay NS_ENUM inicia Xcode 4.5:

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

Y puede considerar NS_OPTIONS si trabaja con indicadores binarios:

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


Ejecuto una prueba en el simulador, por lo que la intención de la prueba es verificar el tamaño de diferentes tipos de enteros. Para eso, el resultado de sizeof se imprimió en la consola. Así que enum valores 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 };

Código de prueba:

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

Resultado para el simulador de iPhone Retina (4 pulgadas) :

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

Resultado para el simulador de iPhone Retina (4 pulgadas 64 bit) :

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

Conclusión

Una enum genérica puede ser un tipo int o unsigned int de 4 bytes para 32 o 64 bits. Como ya sabemos, NSUInteger y NSInteger tienen 4 bytes para 32 bits y 8 bytes en el compilador de 64 bits para iOS.


Estas son dos declaraciones separadas. El typedef garantiza que, cuando usa ese tipo, siempre obtiene un NSUInteger.

El problema con una enumeración no es que no sea lo suficientemente grande como para mantener el valor. De hecho, la única garantía que obtienes para una enumeración es que sizeof (enum Foo) es lo suficientemente grande como para contener los valores que hayas definido actualmente en esa enumeración. Pero su tamaño puede cambiar si agrega otra constante. Es por eso que Apple hace el typedef por separado, para mantener la estabilidad binaria de la API.


No se garantiza que los tipos de datos de las constantes de la enumeración sean NSUInteger , pero se garantiza que se NSUInteger a NSUInteger cada vez que los use a través de NSCellType .

En otras palabras, la declaración decreta que, aunque los valores de la enumeración encajarían en un unsigned int , el almacenamiento reservado para ellos cuando se acceda a través de NSCellType debería ser un NSUInteger .