c - 没有默认大小写的 Objective-C 枚举 "exhaustive"开关的行为

标签 c objective-c enums switch-statement

在 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,并且没有观察到崩溃。但是该方法没有returnreturn 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/

相关文章:

php - Laravel 5 枚举类型在创建时失败

c# - 如何使 JSON.NET StringEnumConverter 使用连字符分隔的大小写

c - 数组声明

c - 解释这个功能

ios - uipageviewcontroller 中的 UITapGestureRecognizer

ios - 使用 objective-c 在iOS中制作一个没有图像的圆形按钮

c - 如何在大于 4 GiB 的地址范围内 malloc

创建一个 Rust 共享库,返回一个函数指针结构到 C 主程序

ios - 在 iPad 中从纵向模式切换到横向模式时如何正确加载图像和按钮

java - 反序列化 Enum,其中包含 Map