在传递给方法时,将相同的枚举成员装箱会生成一个更大的整数

Sas*_*ats 7 boxing objective-c clang objective-c-literals

我正在使用Clang的原始拳击功能来打包枚举成员 NSNumber

Clang doc 的Boxed Enums部分说明了编译器将成员枚举成整数,除非指定了类型.

有趣的是,我得到不同大小的整数,这取决于我将枚举成员传递给方法的方式.我已经能够将案例分解为以下代码

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);
}
Run Code Online (Sandbox Code Playgroud)

控制台输出是

CF编号类型为A:3; B:4

(第一个是kCFNumberSInt32Type,第二个是kCFNumberSInt64Type)

如果我改变声明,typedef enum _MyEnum : int我会看到两个相同的结果:kCFNumberSInt32Type.

为什么整数的大小在两种拳击方法之间有所不同?

jus*_*tin 4

我认为您提供的链接中描述了这种情况:

对枚举类型的值进行装箱将产生一个 NSNumber 指针,该指针具有根据枚举的基础类型创建的方法,该类型可以是固定的基础类型或编译器定义的能够表示该枚举所有成员的值的整数类型。枚举:

typedef enum : unsigned char { Red, Green, Blue } Color;
Color col => Red;
NSNumber *nsCol = @(col); // => [NSNumber numberWithUnsignedChar:]
Run Code Online (Sandbox Code Playgroud)

但图书馆促销的细节并未提及,这就是引入期望差异的地方。

-testA最终打电话+[NSNumber numberWithInt:]

-testB最终打电话+[NSNumber numberWithUnsignedInt:]

因此,您看到的抽象“升级”是因为CFNumber(因此NSNumber)此时实际上不支持无符号值(请参阅枚举常量CFNumberType)——根据您看到的输出,人们会假设NSNumber实现只是升级到下一个有符号值在构造函数的无符号初始值设定项的情况下能够表示所有值的类型 - 显然没有测试该值以查看是否可以应用任何“宽度最小化”。

当然,NSNumber声明以无符号类型作为参数的构造函数和初始化程序,但无符号整数的内部表示实际上存储为有符号整数表示。

当将装箱文字提升为NSNumber. 例如,uint16_t类型化枚举将存储为 32 位 int (通过numberWithUnsignedShort:),并且 int32_t 也是 32 位 int (通过numberWithInt:)。虽然,在值也是已知的情况下-testA,所以也可以在那里调用更合适的构造函数——因此编译器只是基于类型而不是类型和值来最小化宽度。当枚举的类型未指定或指定为无符号类型时,您可能会看到这样的促销。