以下是否有差异?还是它们都一样?
有区别,其中一些是由C导致的,C是Objective-C的基础,还有一个区别是如果您考虑将您的Objective-C代码导入Swift。
您的第一个例子:
enum WeekDays { Monday, ..., Friday };
这是一个原始的C语言enum
。它声明了一个名为enum WeekDays
的类型,底层类型为int
,并包含5个字面值。
C语言不是强类型语言:这里的字面值并不是enum WeekDays
类型的,而是int
类型的;而且enum WeekDays
类型的变量可以被赋予不是Monday
到Friday
的值,并且可以被赋值给int
类型的变量而无需进行强制转换。第一个字面值(这里是Monday
)将具有int
值0
,后续的字面值将比前一个字面值多1(因此Friday
的值为4
)。例如:
enum WeekDays one;
int two;
one = Monday;
two = Tuesday; // assigning enum literal to int is OK
two *= 42; // will produce an int value outside of Monday thru Friday
one = two; // which can be assigned to an enum WeekDays
one = 34; // int values can also be directly assigned
所有的enum
变量都存在这种弱点。然而,包括自Xcode至少v8以来使用的编译器在内的许多编译器,在某些情况下会发出警告(而不是错误),例如如果switch
表达式是一个enum
类型,并且case
标签省略了一些声明的字面值或者具有声明值之外的值。
给字面值赋予显式的值
与其依靠默认值,可以为enum
字面值指定特定的值。例如,将Monday
赋值为1
,将Friday
赋值为5
:
enum WeekDays { Monday = 1, ..., Friday };
通常情况下,没有显式值的文本将被分配比先前的文本字面量多一的值。这些值也不需要连续:
enum WeekDays { Monday = 1, Tuesday = 42, Wednesday = 3, Thursday, Friday }
使用 typedef
很多C语言类型声明很复杂,难以解析(它们是小测验问题的内容),C语言typedef
允许声明一个特定类型的别名。 在使用类型时,typedef
通常与enum
声明一起使用,以避免使用enum
. 例如:
typedef enum WeekDays { Monday, ..., Friday } WeekDays;
声明别名WeekDays
代表enum WeekDays
。例如:
WeekDays one
// declaration for one
enum WeekDays one
在上述声明中两次看到WeekDays
可能会让人感到困惑,或者对某些人来说输入太多了,可以省略:
typedef enum { Monday, ..., Friday } WeekDays;
这与前面的声明稍有不同,WeekDays
现在是一个 匿名 的enum
别名。使用这种类型,第一种声明形式现在已经无效:
enum Weekdays one; // invalid, no such enum
WeekDays one; // valid
更改底层类型
默认情况下,enum
的底层类型为 int
。可以指定不同的整数类型,该类型必须足够大以容纳所有字面值表示的值。整数类型包括整数、无符号整数和字符类型。底层类型是通过在enum
标签后添加 :<type>
来指定的,并且可以为字面值指定相同类型的特定值。例如:
typedef enum Vowels : char { Letter_A = 'a', ..., Letter_U = 'u' } Vowels;
如上所述,可以使用匿名枚举:
typedef enum : char { Letter_A = 'a', ..., Letter_U = 'u' } Vowels;
使用 NS_ENUM
NS_ENUM
是一个(苹果公司)Objective-C方便的宏,会扩展成一个typedef
和enum
声明。例如:
typedef NS_ENUM(NSInteger, WeekDays) { Monday, ..., Friday };
扩展为:
typedef enum WeekDays : NSInteger WeekDays;
enum WeekDays : NSInteger { Monday, ..., Friday };
避免前置声明等同于:
typedef enum WeekDays : NSInteger { Monday, ..., Friday } WeekDays;
使用Xcode v8或更高版本,使用NS_ENUM
和直接使用typedef
声明枚举类型时,在编译器检查和警告方面没有任何区别。
NS_ENUM
的作用
如果您编写的Objective-C代码将被Swift使用,则使用NS_ENUM
确实会有所不同:使用NS_ENUM
声明的枚举将被导入为Swift enum
;而直接声明的枚举将被导入为Swift struct
及其包含的全局只读计算属性集合。为每个枚举值按一个属性生成一个结构体。
苹果目前的文档(使用Swift与Cocoa和Objective-C(Swift 4.0.3),通过)中未解释这种差异的原因。
最后,回到你的例子
上述内容应该可以让你理解它们之间的差异:
默认的C枚举,底层类型是int
,作为enum WeekDays
类型使用。
别名C枚举,底层类型为NSUinteger
,作为WeekDays
类型使用。
使用NS_ENUM
生成的枚举,底层类型为NSInteger
,可以使用WeekDays
或enum WeekDays
引用此类型。
这声明了两个不同的类型,可能会产生警告。第一个是没有别名的匿名enum
,因此稍后不能引入带有此类型的变量。它引入的值是int
类型。第二个是NSInteger
的别名。在64位系统上,NSInteger
是64位的,而int
是32位的,因此在字面值类型(int
)和"枚举"类型(WeekDays
)之间进行赋值可能会产生警告。进一步的switch
语句将不像前三个版本一样被检查,因为它们仅基于NSInteger
。该版本看起来像是错误地模仿NS_ENUM
,不应该使用它(尽管合法,但并不实现它所暗示的功能)。
希望以上内容比混淆更加清楚明白!
NS_ENUM
是Obj-C中特别可用的方便宏。也许你应该研究一下typedef
的语义,而不仅仅关注于enum
。 - cwschmidt