39

tl;博士版本

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

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

NSUInteger 的 typedef 似乎没有以任何方式与 enum 声明相关联。

完整版本

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

枚举(enum)常量的一个问题是它们的数据类型经常是不确定的。换句话说,枚举常量是不可预测的 unsigned int。使用传统构造的枚举,编译器实际上会根据它找到的内容来设置底层类型。基础类型可以是(有符号的)int 甚至 long。举个例子:

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

编译器查看此声明,并找到分配给成员常量之一的负值,声明枚举 int 的基础类型。如果成员的值范围不适合 int 或 unsigned int,则基本类型默默地变为 64 位(long)。因此,定义为枚举的基本类型的量可以悄悄地改变大小以符合枚举中的值。无论您是编译 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 似乎没有明确地与枚举声明绑定,如何知道它们的数据类型是 unsigned int 还是 NSUInteger?

4

4 回答 4

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,
};
于 2012-11-13T12:33:43.683 回答
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-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可以是32 位或 64 位的 4 字节intunsigned int类型。正如我们已经知道的那样,iOS 的 32 位编译NSUIntegerNSInteger是 4 个字节,64 位编译器是 8 个字节。

于 2014-01-07T01:30:51.383 回答
8

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

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

于 2011-12-13T22:19:33.273 回答
2

枚举常量的数据类型不保证是NSUInteger,但保证NSUInteger每次使用它们时都会被强制转换为NSCellType

换句话说,声明规定,虽然枚举的值当前可以放入 an unsigned int,但在通过访问时为它们保留的存储空间NSCellType应该是 an NSUInteger

于 2011-12-14T09:37:21.620 回答