Ошибка 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?