Поведение «исчерпывающего» переключателя перечисления Objective-C без случая по умолчанию - PullRequest
0 голосов
/ 23 января 2019

В 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
}

При включении перечисления компилятор предупредит вас, если регистр перечисления не обрабатывается:

Значение перечисления 'третий' не обрабатывается в переключателе

Но как только все случаи были обработаны, аналогичного предупреждения о том, что случай «по умолчанию» все еще возможен для недопустимых членов перечисления, нет.

Каково поведение в такой ситуации? Кажется, что метод NSString возвращает nil, и сбоя не наблюдается. Но у этого метода нет return. return nil генерируется автоматически и при каких обстоятельствах?

Обратите внимание, что операторы кода после «исчерпывающего» переключателя не приводят к предупреждению, которое обычно генерируется:

Код никогда не будет выполнен

1 Ответ

0 голосов
/ 23 января 2019

TL; DR , если ни один из случаев не совпадает, функция возвращает управление вызывающей стороне, но возвращается значение undefined .

Заявление C / ObjC return принципиально делает две вещи.Это приводит к тому, что его значение помещается в определенное место, так что вызывающий объект знает, где его искать.(Место, определяемое платформой / языком ABI.) Затем он перемещает элемент управления обратно к вызывающей функции (путем извлечения адреса из стека и перехода к нему).

В этом случае элемент управления собираетсяпроходите мимо конца switch, и никакой return устав не будет выполнен.Компилятор делает , однако выдает переход к концу метода.

(На самом деле - я не очень хорош в чтении ассемблера, но - аннотированный ассемблер с отладочной информациейя вижу, что Xcode 10.1 создает для вашего кода все последовательности переключений в одну точку выхода для метода. И эта точка выхода также достигнута, если ни одно из сравнений не выполнено успешно.)

Но мы перейдем к этому переходу, не поместив никакого значения в регистр возврата, что означает, что оно такое же, как и любое другое неинициализированное значение - мусор.Тот факт, что вы надежно получаете nil, вероятно, объясняется простотой вашей тестовой программы или созданием конфигурации отладки.

...