Значения перечисления: NSInteger или int?
tl; dr Версия
Как типы данных констант перечисления гарантированно являются NSUInteger вместо unsigned int при объявлении перечисления таким образом:
enum {
NSNullCellType = 0,
NSTextCellType = 1,
NSImageCellType = 2
};
typedef NSUInteger NSCellType;
typedef для NSUInteger никак не привязывается к объявлению перечисления.
Полная версия
Я читал через Apple 64-битное руководство по переходу для Cocoa для некоторых рекомендаций по значениям перечисления, и я ушел с вопрос. Здесь (длинная) цитата из раздела Enumeration Constants, основное внимание:
Проблема с константами перечисления (enum) заключается в том, что их типы данных часто являются неопределенными. Другими словами, константы континуума не являются предсказуемыми беззнаковыми int.. С условно сконструированными перечислениями компилятор фактически устанавливает базовый тип в зависимости от того, что он находит. Основной тип может быть (подписан) int или даже длинным. Возьмем следующий пример:
type enum {
MyFlagError = -1,
MyFlagLow = 0,
MyFlagMiddle = 1,
MyFlagHigh = 2
} MyFlagType;
Компилятор просматривает это объявление и, обнаруживая отрицательное значение, назначенное одной из констант-членов, объявляет базовый тип перечисления int. Если диапазон значений для членов не вписывается в int или unsigned int, тогда базовый тип становится 64-битным (длинным). Таким образом, базовый тип величин, определяемый как перечисления, может изменять молчащий размер, чтобы соответствовать значениям в перечислении. Это может произойти, если вы компилируете 32-разрядные или 64-разрядные. Излишне говорить, что эта ситуация создает препятствия для совместимости двоичных файлов.
В качестве средства для решения этой проблемы Apple решила более подробно описать тип перечисления в API Cocoa. Вместо объявления аргументов в терминах перечисления файлы заголовков теперь отдельно объявить тип для перечисления, размер которого можно указать. Члены перечисления и их значения объявляются и назначаются по-прежнему. Например, вместо этого:
typedef enum {
NSNullCellType = 0,
NSTextCellType = 1,
NSImageCellType = 2
} NSCellType;
теперь есть следующее:
enum {
NSNullCellType = 0,
NSTextCellType = 1,
NSImageCellType = 2
};
typedef NSUInteger NSCellType;
Тип перечисления определяется в терминах NSInteger или NSUInteger, чтобы сделать базовый тип перечисления 64-разрядным для 64-разрядных архитектур.
Мой вопрос заключается в следующем: учитывая, что typedef явно не привязан явно к объявлению enum, как узнать, являются ли их типы данных неподписанными int или NSUInteger?
Ответы
Ответ 1
Теперь существует NS_ENUM
запуск Xcode 4.5:
typedef NS_ENUM(NSUInteger, NSCellType) {
NSNullCellType = 0,
NSTextCellType = 1,
NSImageCellType = 2
};
И вы можете рассмотреть NS_OPTIONS
, если вы работаете с двоичными флагами:
typedef NS_OPTIONS(NSUInteger, MyCellFlag) {
MyTextCellFlag = 1 << 0,
MyImageCellFlag = 1 << 1,
};
Ответ 2
Я запускаю тест на симуляторе, поэтому целью теста является проверка размера разных целых типов. Для этого результат sizeof
был напечатан в консоли. Поэтому я тестирую значения 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
};
Тестовый код:
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));
Результат для iPhone Retina (4-inch) Simulator:
sizeof enum: 4
sizeof enum negative: 4
sizeof enum NSUInteger: 4
sizeof enum NSInteger: 4
Результат для iPhone Retina (4-дюймовый 64-битный) симулятор:
sizeof enum: 4
sizeof enum negative: 4
sizeof enum NSUInteger: 8
sizeof enum NSInteger: 8
Заключение
Общий enum
может быть int
или unsigned int
типами 4 байта для 32 или 64 бит.
Как мы уже знаем, NSUInteger
и NSInteger
составляют 4 байта для 32 бит и 8 байтов в 64-битном компиляторе для iOS.
Ответ 3
Это два отдельных объявления. Typedef гарантирует, что при использовании этого типа вы всегда получаете NSUInteger.
Проблема с перечислением заключается не в том, что он недостаточно велик, чтобы удерживать значение. Фактически, единственная гарантия, которую вы получаете для перечисления, заключается в том, что sizeof (enum Foo) достаточно велик, чтобы удерживать все значения, которые вы в настоящее время определили в этом перечислении. Но его размер может измениться, если вы добавите еще одну константу. Вот почему Apple делает отдельный typedef, чтобы поддерживать бинарную стабильность API.
Ответ 4
Типы данных констант перечисления не гарантируются NSUInteger
, но они гарантированно будут отбрасываться до NSUInteger
каждый раз, когда вы используете их через NSCellType
.
Другими словами, в декларации указывается, что, хотя значения перечисления в настоящее время будут вписываться в unsigned int
, зарезервированное для них хранилище при доступе через NSCellType
должно быть NSUInteger
.