为什么Cocoa不在所有地方都使用相同的枚举声明风格?

12

我想知道cocoa中枚举声明不同风格的背后原理是什么?

像这样:

enum { constants.. }; typedef NSUInteger sometype;

为什么要使用typedef来避免NSUInteger赋值时需要进行强制类型转换?

有时候typedef是NSInteger/NSUInteger之一,那么为什么不总是使用NSInteger呢?使用NSUInteger是否有真正的好处?

枚举标记有时仍然被使用,比如在_NSByteOrder这里

这个回答也非常有用:What is a typedef enum in Objective-C?


1
typedef和enum非常不同...我不太明白你的问题?enums用于设置命名整数常量的集合。typedef用于给数据类型取一个新名称,通常是为了支持不同的架构... - t00ny
3个回答

12

有几个原因:

原因1:灵活性:

enum lickahoctor { yes = 0, no = 1, maybe = 2 };

声明一个枚举类型。你可以在任何地方使用yesnomaybe这些值,并将它们赋值给任何整数类型。你也可以将其作为一种类型来使用,写法如下:

enum lickahoctor myVar = yes;
这样做很好,因为如果一个函数需要一个类型为 "enum lickahoctor" 的参数,你就知道可以将 yesnomaybe 分配给它。此外,调试器也会知道,所以它会显示符号名称而不是数值。问题在于,编译器只允许你将你在 enum lickahoctor 中定义的值分配给 myVar。例如,如果你想在基类中定义一些标志,然后在子类中添加一些标志,就不能用这种方式。
如果你使用一个 int,就不会有这个问题。所以你想使用某种 int,这样你就可以分配任意常数。
原因 2: 二进制兼容性:
编译器选择适合所有你在枚举中定义的常量的大小。你无法保证你会得到什么。因此,如果你直接将包含这样一个变量的结构体写入文件,则不能保证在下一个版本的应用程序中读取时它仍然是相同的大小(至少根据 C 标准如此 -- 在实践中并不完全是这样)。
如果你使用某种 int,平台通常会保证该数字具有特定的大小。特别是如果你使用了那些保证具有特定大小的类型之一,比如 int32_t/uint32_t
原因 3: 可读性和自我记录
当你声明上面的 myVar 时,可以立即知道可以将哪些值放入其中。如果你只是使用一个 int 或一个 uint32_t,那就不是这样了。所以你要使用
enum { yes, no, maybe };
typedef uint32_t lickahoctor;

为整数定义一个好的名称,放在常量附近,以提醒人们此类型的变量可以存储这个值。但是您仍然可以获得可预测的、固定大小和在需要时定义子类中的其他值的优势。

原因4: 支持位字段

枚举类型的变量只支持从其选项中精确赋值一个值。因此,如果您尝试实现位字段,则不能将其类型定义为枚举类型。此外,您需要使用无符号变量来避免符号扩展带来的问题。


NSInteger并不能保证具有特定的大小,因为在i386x86_64上它们的大小是不同的。如果你在Core Duo上写入一个带有lickahoctor的二进制文件,然后升级你的Mac并重新读取它,那么就会出现一些滑稽的事情。 - user23743
当然,Graham。我脑抽了。感谢你纠正了它。 - uliwitness
为了方便搜索本篇文章的人,我已将对 NSInteger 的提及更改为上面的 uint32_t。不过,Apple 经常使用 NSInteger,这意味着在 32 位系统上你会得到 int32_t,在 64 位系统上则会得到 int64_t。这意味着编译为 32 位和 64 位的代码是二进制不兼容的(无法写入磁盘),但无论将来添加多少个常量到枚举中,它都是可靠的。 - uliwitness

2
使用typedef的原因是为了指定枚举值的基础类型。只要截断值,就可以将枚举值转换为另一种类型,通过将其强制转换为较小的类型(从NSUInteger到unsigned short)。
NSInteger和NSUInteger被引入是为了简化应用程序的64位迁移,通过提供一个体系结构/平台无关的有符号和无符号整数类型。这样,无论CPU有多少位,应用程序都不需要重新编写。
有时typedef是NSInteger/NSUInteger中的任意一种,为什么不总是使用NSInteger?使用NSUInteger是否真的有好处?
选择取决于枚举中的值。一些枚举具有许多值,因此需要所有可用的位:
- NSInteger提供2^31个正负值(在32位体系结构上)。 - NSUInteger提供2^32个正值(在32位体系结构上)。 - 如果您的枚举只包含正值,请使用NSUInteger。 - 如果您的枚举包含正值和负值,请使用NSInteger。 - NSUInteger通常用于标志枚举,因为它提供了32个不同的值(在32位体系结构上)可随意组合。
我不知道苹果开发团队在选择方面是否有规则。希望如此...

你是指2的31次方和32次方,对吗?此外,这两种类型都提供了2的32次方个不同的值;区别在于有符号类型(例如NSInteger)中有一半(2的31次方)是负数,大约一半(2的31次方-1)是正数。使用无符号类型,所有2的32次方个值都是非负的。这就是真正的区别:如果你所有的枚举值都将是正数,你可以将类型声明为NSUInteger。相反,如果你将有负值,你必须使用NSInteger - Peter Hosey
你是正确的。我指的是标志值。我已经相应地更新了我的答案。 - Laurent Etiemble

1

虽然你可以使用类似的东西

  typedef enum { constants... } sometype;

数据类型的最终位大小不能保证。嗯,这并不严谨,但足够正确。API最好以具体的数据大小来定义,而不是依赖于编译器设置可能会变化的东西。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接