在 Swift 中,init(rawValue:)
系统确保将 Int
转换为枚举要么导致有效的枚举大小写,要么导致 nil
.
在 Objective-C 中没有这样的安全性,可以通过强制转换非成员“rawValue”来创建无效的枚举成员。
typedef NS_ENUM(NSInteger, ExampleEnum) {
first = 0,
second,
third,
};
+ (NSString *)stringForCase:(ExampleEnum)enumCase {
switch (enumCase) {
case first: return @"first";
case second: return @"second";
case third: return @"third";
}
}
+ (void)testEnum {
ExampleEnum invalidCase = (ExampleEnum)3; // this "rawValue" is out of bounds
NSString *string = [self stringForCase:invalidCase]; // nil
}
打开枚举时,如果未处理枚举情况,编译器会警告您:
Enumeration value 'third' not handled in switch
但是一旦处理了所有情况,就没有类似的警告,即对于无效的枚举成员仍然可能出现“默认”情况。
在这种情况下的行为是什么? NSString 方法似乎返回 nil
,并且没有观察到崩溃。但是该方法没有return
。 return nil
是在什么情况下自动生成的?
请注意,“exhaustive”开关之后的代码语句不会导致通常会生成的警告:
Code will never be executed
最佳答案
TL;DR,如果所有情况都不匹配,则该函数确实将控制权返回给调用者,但返回值是未定义 .
C/ObjC return
语句基本上做了两件事。它使它的值被放到一个特定的地方,这样调用者就知道在哪里寻找它。 (由平台/语言 ABI 定义的位置。)然后它将控制移回调用函数(通过从堆栈中弹出一个地址并跳转到它)。
在这种情况下,控件将直接越过 switch
的末尾,并且不会执行任何 return
语句。然而,编译器确实发出一个跳转到方法的末尾。
(事实上——我不擅长阅读汇编程序,但是——我看到 Xcode 10.1 为你的代码创建的带调试注释的汇编程序将所有 switch 案例汇集到该方法的单个导出点。那如果没有一个案例比较成功,则也到达导出点。)
但是我们在没有将任何值放入返回寄存器的情况下到达那个跳转,这意味着它与任何其他未初始化的值相同 - 垃圾。您可靠地获得 nil
的事实可能是由于您的测试程序的简单性和调试配置中的构建。
关于c - 没有默认大小写的 Objective-C 枚举 "exhaustive"开关的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54318565/