枚举值:NSInteger还是int?

时间:2011-12-13 22:07:48

标签: objective-c cocoa enums typedef nsinteger

tl; dr Version

在声明枚举时,如何确保枚举常量的数据类型为NSUInteger而不是unsigned int:

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

NSUInteger的typedef似乎没有以任何方式绑定到枚举声明。

完整版

我正在阅读Apple的 64-Bit Transition Guide for Cocoa ,以获取有关枚举值的一些指导,然后我就提出了一个问题。这是枚举常量部分的一个(冗长的)引用,强调我的:

  

枚举(枚举)常量的一个问题是它们的数据类型经常是不确定的。换句话说,枚举常量不是可预测的无符号整数。对于常规构造的枚举,编译器实际上根据它找到的内容设置基础类型。底层类型可以是(signed)int或甚至long。请看以下示例:

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

编译器查看此声明,并查找分配给其中一个成员常量的负值,声明枚举int的基础类型。如果成员的值范围不适合int或unsigned int,则基类型将静默地变为64位(长整数)。因此,定义为枚举的基本类型数量可以无提示地更改大小以符合枚举中的值。无论您是编译32位还是64位,都会发生这种情况。不用说,这种情况对二进制兼容性提出了障碍。

     

作为解决此问题的补救措施,Apple决定更明确地了解Cocoa API中的枚举类型。现在,头文件分别声明了一个枚举文件,而不是根据枚举声明参数。可以指定大小的枚举的类型。枚举的成员及其值将像以前一样声明和分配。例如,而不是:

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

现在有这个:

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

枚举类型是根据NSInteger或NSUInteger定义的,以使基本枚举类型在64位体系结构上具有64位功能。

我的问题是:鉴于typedef似乎没有明确地与枚举声明绑定,如何知道他们的数据类型是无符号整数还是NSUInteger?

4 个答案:

答案 0 :(得分:53)

现在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,
};

答案 1 :(得分:17)

我在模拟器上运行测试,因此测试的目的是检查不同整数类型的大小。为此,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英寸)模拟器的结果


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可以是32个或64位的intunsigned int类型的4个字节。 我们已经知道NSUIntegerNSInteger是32位的4个字节和64位64位编译器中的8个字节。

答案 2 :(得分:7)

这是两个单独的声明。 typedef保证,当你使用该类型时,你总是得到一个NSUInteger。

枚举的问题并不在于它不足以容纳值。事实上,你得到枚举的唯一保证是sizeof(enum Foo)足够大,可以容纳你目前在枚举中定义的任何值。但是如果添加另一个常量,它的大小可能会改变。这就是为什么Apple做单独的typedef,以维持API的二进制稳定性。

答案 3 :(得分:2)

枚举常量的数据类型不能保证为NSUInteger,但每次通过NSUInteger使用它们时,它们都会保证投放到NSCellType

换句话说,声明规定尽管枚举的值当前适合unsigned int,但通过NSCellType访问时为它们保留的存储应该是NSUInteger