枚举值:NSInteger或int?
tl; dr版本
当枚举常量的数据types如此声明一个枚举时,如何保证是NSUInteger而不是unsigned int:
enum { NSNullCellType = 0, NSTextCellType = 1, NSImageCellType = 2 }; typedef NSUInteger NSCellType;
NSUInteger的typedef似乎没有以任何方式绑定到枚举声明。
完整版
我正在通过苹果的64位cocoa过渡指南阅读一些枚举值的指导,我拿出一个问题。 下面是枚举常量部分(冗长)的引用,重点是我的:
枚举(enum)常量的一个问题是它们的数据types经常是不确定的。 换句话说,枚举常量不是可预测的unsigned int。 使用传统构造的枚举,编译器实际上根据它find的内容设置基础types。 基础types可以是(signed)int或甚至long。 以下面的例子:
type enum { MyFlagError = -1, MyFlagLow = 0, MyFlagMiddle = 1, MyFlagHigh = 2 } MyFlagType;
编译器查看这个声明,并find一个赋值给一个成员常量的负值,声明枚举int的基本types。 如果成员的值的范围不适合int或unsigned int,那么基types默默地变成64位(长)。 定义为枚举的数量的基types因此可以静默地改变大小以符合枚举中的值。 无论您是在编译32位还是64位,都可能发生这种情况。 不用说,这种情况给二进制兼容性带来了障碍。
为了解决这个问题,苹果决定在Cocoa API中更加明确枚举types。 现在头文件不是按照枚举来声明参数,而是单独声明枚举的types,这个枚举的大小可以被指定。 列举的成员及其价值如前所述被宣布和分配。 例如,而不是这个:
typedef enum { NSNullCellType = 0, NSTextCellType = 1, NSImageCellType = 2 } NSCellType;
现在有这个:
enum { NSNullCellType = 0, NSTextCellType = 1, NSImageCellType = 2 }; typedef NSUInteger NSCellType;
枚举types是根据NSInteger或NSUInteger定义的,以便在64位体系结构上使基本枚举types成为64位。
我的问题是这样的:鉴于typedef似乎没有明确地绑定到枚举声明,如何知道他们的数据types是无符号整型还是NSUInteger?
现在有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, };
我在模拟器上运行一个testing,所以testing的目的是检查不同整数types的大小。 为此, sizeof
的结果被打印在控制台中。 所以我testing这个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 };
testing代码:
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视网膜(4英寸)模拟器的结果 :
sizeof enum: 4 sizeof enum negative: 4 sizeof enum NSUInteger: 4 sizeof enum NSInteger: 4
iPhone视网膜(4英寸64位)模拟器的结果 :
sizeof enum: 4 sizeof enum negative: 4 sizeof enum NSUInteger: 8 sizeof enum NSInteger: 8
结论
genericsenum
可以是32位或64位的4字节的int
或unsigned int
types。 正如我们已经知道NSUInteger
和NSInteger
是32位的4个字节和iOS的64位编译器的8个字节。
这是两个单独的声明。 typedef保证,当你使用这个types时,你总是得到一个NSUInteger。
枚举的问题并不在于它不足以保存值。 实际上,为枚举获得的唯一保证就是sizeof(枚举Foo)足够大,以保存当前在该枚举中定义的任何值。 但是如果添加另一个常量,其大小可能会改变。 这就是为什么苹果做单独的typedef,以保持API的二进制稳定性。
枚举的常量的数据types不能保证是NSUInteger
,但是每次通过NSCellType
使用它们时,都保证将其转换为NSUInteger
。
换句话说,声明规定虽然枚举的值当前适合于一个unsigned int
,但是当通过NSCellType
访问时为它们保留的存储应该是一个NSUInteger
。