Ошибка Weird Switch в Obj-C

У меня есть оператор 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'

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

Oh, а buttonIndex - NSInteger

Ответ 2

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

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

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

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

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

Некоторая история. Раньше C разрешало вам объявлять "блокировать локальные" переменные в начале блока, за которым последовали различные утверждения. C99 изменил ситуацию, чтобы вы могли свободно смешивать объявления переменных и утверждения.

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

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

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

(1) Это не должно быть технически точным описанием, но разумным приближением для тех, кто не знаком с проблемами.

EDIT:

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

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

Ответ 3

Одна вещь, которую мне нравится делать в этой ситуации, - это разместить содержимое каждого 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];
    }
}

Ответ 4

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

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

EDIT: В языках C вы не можете инициировать объекты в коммутаторе, если вы не ставите каждый "случай" между {} s

Ответ 5

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

Итак:

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

Ответ 6

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

Ответ 7

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