Вопрос по objective-c, iphone, switch-statement – Странная ошибка переключения в Obj-C

17

У меня есть этот оператор switch в моем коде:

switch(buttonIndex){
      case 0:
         [actionSheet dismissWithClickedButtonIndex:buttonIndex animated:YES];
         break;
    case 1:
        UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
        imagePicker.delegate = self;
        imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
        [self presentModalViewController:[imagePicker autorelease] animated:YES];
        break;
    default:
        [self openEmailViewInViewController:self];
}

И при создании экземпляра UIImagePickerController в случае 1 я получаю ошибку:

error:expected expression before 'UIImagePickerController'

и я понятия не имею, что я делаю неправильно. Мысли?

О, и ButtonIndex является NSInteger

Ваш Ответ

7   ответов
64

Я столкнулся с этой проблемой, и однажды я решил разобраться с ней.

Краткое без ответа, но прагматичное решение:

Способ обойти эту «проблему» это использовать точку с запятой,;сразу после толстой кишкиcase ...: заявление. Например, используя приведенный вами пример, он может быть «исправлен». поэтому он компилируется и ведет себя так, как вы ожидаете интуитивно:

    case 1:; // <- Note semi-colon.
            UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
            imagePicker.delegate = self;

Длинный ответ:

Немного истории: Раньше C разрешал вам только объявлять & quot; блокировать локальные & quot; переменные в начале блока, за которыми следовали различные операторы. C99 изменил положение вещей, чтобы вы могли свободно смешивать объявления и операторы переменных.

В контексте грамматики C99 BNF объявление переменной являетсяdeclarationи заявление являетсяstatement,statement означает несколько вещей, одна из которых известна какcompound-statementзнакомая{ ... } блок.... часть свободно определяется какzero or more block-itemsсblock-item определяется какeither a declaration or a statement.

Проблема заключается в том, какlabeled-statement (метка перехода, метка кейса илиdefault:по сути...: заявления) определяется, что является свободно определенным...: zero or more statements, Это не так, как можно было бы интуитивно ожидать,zero or more statements or declarations, Использование; сразу послеlabeled-statements: по сути прекращаетzero or more statements частьlabeled-statement, Это заставляет грамматику отступать кcompound-statement определение, которое допускает следующее «утверждение» быть либоstatement или жеdeclaration.

Я не исследовал, является ли это непреднамеренным упущением спецификации языка C99 (на практике это ошибка в стандарте C99), или это прагматическая уступка сложностям написания языковых грамматик. Если вы не знакомы с написанием грамматик, вы заметите, что приведенное выше объяснение допускает рекурсию: Alabeled-statement может соответствоватьcase 1: case 2: case 3:, Слишком упрощенно(1)некоторые типы грамматической рекурсии являются простыми и «однозначными», в то время как другие являются сложными и «неоднозначными». Для простоты большинство языковых инструментов будет обрабатывать только тот случай, когда любая неоднозначность должна быть детерминировано разрешена, если не рассматривать ничего, кроме «следующего токена». Я упоминаю об этом только потому, что, хотя это может показаться интуитивно понятным недостатком спецификации C99, могут существовать неопровержимые причины, по которым это существует ... и я не потрудился провести какие-либо дальнейшие исследования по этому вопросу, чтобы выяснить это. в любом случае.

(1) This is not meant to be a technically accurate description, but a reasonable approximation for those not familiar with the issues involved.

EDIT:

Решение, которое я дал, работает в «Most». случаи (случаи «использования», неswitch cases), но это не сработает в одном случае: это не будет работать, когда объявление объявляет C99variable length array, такие какcase 1:; void *ptrs[count];  Это связано с тем, что в C99 было ошибкой «перепрыгивать» объявление VLA C99, находящегося в той же лексической области, где произошел переход. В этих случаях вам нужно использоватьcase 1: { void *ptrs[count]; }, В этом случае сфера действияptrs VLA заканчивается на закрытии}, Это сложнее, чем кажется на первый взгляд, потому что нижеприведенный код является абсолютно допустимым, хотя на первый взгляд интуитивно кажется, что это не так:

switch(3){
  case 0:
    printf("case 0\n");
    break;
  case 1:;
    int *ip = NULL;
    printf("case 1\n");
    break;
  case 2:
    {
      int ia[6];
      printf("case 2\n");
      break;
      case 3:
        printf("case 3\n");
        break;
      default:
        printf("default\n");
    }
}

Это компилирует, и при запуске печатаетcase 3.

Смотрите также:Википедия: Duffs Device

& lt; начинается медленно хлопать & gt;
Ну, это намного больше, чемI знал раньше. Я просто знал, что по какой-то причине C требует под-области видимости, если вы хотите поместить объявление в кейс. Благодарю.
Чувак, ты мой герой. Я предпочитаю использовать переключатели гораздо больше, чем операторы if-else, и я никогда не мог понять, почему я получаю ошибку компиляции в obj-c всякий раз, когда пытаюсь ее использовать. Спасибо за ответ!
Черт возьми, это хороший ответ.
3

У меня такая проблема иногда.

Как ни странно, после добавления NSLog до появления ошибки, она работает

EDIT: В языках Си вы не можете создавать экземпляры объектов в переключателе, если не ставите каждый «регистр» между {} с

24
+1 Однако вы можете создать вложенную область видимости, используя {...}, чтобы обернуть операторы для метки случая.
5

В этой ситуации мне нравится делать содержимое каждогоcase в фигурных скобках. Это разрешено компилятором:

switch (buttonIndex)
{
    case 0:
    {
        [actionSheet dismissWithClickedButtonIndex:buttonIndex animated:YES];
        break;
    }
    case 1:
    {
        UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
        imagePicker.delegate = self;
        imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
        [self presentModalViewController:[imagePicker autorelease] animated:YES];
        break;
    }
    default:
    {
        [self openEmailViewInViewController:self];
    }
}
Спасибо за ваш ответ, этот тип простоты делает этот сайт таким полезным! Так держать.
Примечание: в C вы можете поставить дополнительные скобки практически в любом месте, при условии, что правильное вложение сохраняется. if (foo) {{{{{}}}}} // совершенно законно
1

Я видел эту проблему раньше. Это имеет отношение к меткам в C, и будет применяться к меткам, используемым для gotos. Case1 :, case2: строки являются метками. По какой-либо причине первое утверждение после метки не должно быть объявлением. Я проведу некоторое исследование и дополню информацией, если кто-то другой не даст хорошего ответа.

0

Хорошо, если я добавлю объявление переменной перед оператором switch, оно будет работать нормально. Я предполагаю, что объявления переменных не являются «выражениями» в цели C?

2

Ну, это не правильный ответ, так как я не знаю, почему вы видите эту ошибку, но лучшее решение, чем перенести объявление за пределы блока переключателей, - это сделать явные области действия в отдельных случаях блока переключателей. Это обычно решает любую такую проблему с операторами switch, что немного странно в том смысле, что операторы case разделяют область действия.

Так:

switch (foo) {
   case 0: {
      // notice explicit scope here
      break;
   }
   default: {
      // here as well
   }
}

Похожие вопросы