Вопрос по objective-c, iphone, switch-statement – Странная ошибка переключения в 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'
и я понятия не имею, что я делаю неправильно. Мысли?
О, и ButtonIndex является NSInteger
Я столкнулся с этой проблемой, и однажды я решил разобраться с ней.
Краткое без ответа, но прагматичное решение:
Способ обойти эту «проблему» это использовать точку с запятой,;
сразу после толстой кишки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-statement
s:
по сути прекращает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
case
s), но это не сработает в одном случае: это не будет работать, когда объявление объявляет 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
В этой ситуации мне нравится делать содержимое каждого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, и будет применяться к меткам, используемым для gotos. Case1 :, case2: строки являются метками. По какой-либо причине первое утверждение после метки не должно быть объявлением. Я проведу некоторое исследование и дополню информацией, если кто-то другой не даст хорошего ответа.
Ну, это не правильный ответ, так как я не знаю, почему вы видите эту ошибку, но лучшее решение, чем перенести объявление за пределы блока переключателей, - это сделать явные области действия в отдельных случаях блока переключателей. Это обычно решает любую такую проблему с операторами switch, что немного странно в том смысле, что операторы case разделяют область действия.
Так:
switch (foo) {
case 0: {
// notice explicit scope here
break;
}
default: {
// here as well
}
}