Заключение в бокс одного и того же члена перечисления выдает большее целое число, когда оно передается методу
Я использую примитив-боксерскую функцию Clang, чтобы упаковать член перечисления в NSNumber
Раздел Boxed Enums документа Clang об этом говорит, что компилятор помещает члены перечисления в целые числа, если тип не указан.
Как ни странно, я получаю целые числа разных размеров в зависимости от способа передачи члена перечисления методу. Я был в состоянии выделить случай до следующего кода
typedef enum _MyEnum {
MyEnumMember1 = 1000
} MyEnum;
- (void)testEnumerationBoxing
{
NSNumber *numberA = [self testA];
NSNumber *numberB = [self testB:MyEnumMember1];
CFNumberType numberTypeA = CFNumberGetType((__bridge CFNumberRef) numberA);
CFNumberType numberTypeB = CFNumberGetType((__bridge CFNumberRef) numberB);
NSLog(@"CF Number type for A: %lu; B: %lu", numberTypeA, numberTypeB);
}
- (NSNumber *)testA
{
return @(MyEnumMember1);
}
- (NSNumber *)testB:(MyEnum)enumMember
{
return @(enumMember);
}
Консольный вывод
Тип номера CF для A: 3; Б: 4
(первый kCFNumberSInt32Type
второй kCFNumberSInt64Type
)
Если я изменю декларацию на typedef enum _MyEnum : int
Я вижу одинаковый результат для обоих: kCFNumberSInt32Type
,
Почему размер целого числа отличается между двумя методами бокса?
2 ответа
Я считаю этот случай описанным в приведенной вами ссылке:
Упаковка значения типа enum приведет к появлению указателя NSNumber с методом создания в соответствии с базовым типом перечисления, который может быть фиксированным базовым типом или целочисленным типом, определяемым компилятором, который может представлять значения всех членов перечисление:
typedef enum : unsigned char { Red, Green, Blue } Color; Color col => Red; NSNumber *nsCol = @(col); // => [NSNumber numberWithUnsignedChar:]
но детали продвижения в библиотеках не рассматриваются, и именно здесь вводится разница в ожиданиях.
-testA
в итоге звонит +[NSNumber numberWithInt:]
-testB
в итоге звонит +[NSNumber numberWithUnsignedInt:]
Таким образом, абстрактное "продвижение" вы видите, потому что CFNumber
(и следовательно NSNumber
) на самом деле не поддерживают значения без знака в настоящее время (см. константы CFNumberType
enums) - основываясь на выводе, который вы видите, можно предположить NSNumber
Реализации просто переходят к следующему знаковому типу, способному представлять все значения в случае беззнакового инициализатора конструктора - очевидно, без проверки значения, чтобы увидеть, можно ли применить "минимизацию ширины".
Конечно, NSNumber
объявляет конструкторы и инициализаторы, которые принимают беззнаковые типы в качестве параметров, но внутреннее представление целого числа без знака фактически сохраняется как целочисленное представление со знаком.
Компилятор, кажется, вызывает соответствующие / точные конструкторы удобства при продвижении упакованного литерала в NSNumber
, Например, uint16_t
типизированное перечисление будет сохранено как 32-битное целое (через numberWithUnsignedShort:
), а int32_t также является 32-битным int (через numberWithInt:
). Хотя, в случае -testA
значение также известно, поэтому здесь также может быть вызван более подходящий конструктор, поэтому компилятор минимизирует ширину только на основе типа, а не типа и значения. если тип перечисления не указан или указан как тип без знака, вы можете увидеть подобные акции.
Проблема с константами перечисления (enum) заключается в том, что их типы данных часто не определены. Другими словами, константы перечисления не являются предсказуемо неподписанными int.
Надеюсь, это поможет!