Obj-C中的奇怪切换错误

时间:2009-07-24 22:48:33

标签: iphone objective-c switch-statement

我的代码中有这个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];
}

在案例1中的UIImagePickerController实例化中,我收到错误:

error:expected expression before 'UIImagePickerController'

我不知道我做错了什么。想法?

哦,buttonIndex是一个NSInteger

7 个答案:

答案 0 :(得分:64)

我遇到了这个问题,有一天我决定深入研究它。

简短的非答案但务实的解决方案:

解决这个“问题”的方法是在;语句的冒号后面使用分号case ...:。例如,使用您提供的示例,它可以“修复”,因此它可以按照您的直观预期进行编译和操作:

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

答案很长:

某些历史记录:以前,C只允许您在块的开头声明“阻止本地”变量,然后是各种语句。 C99改变了一些东西,所以你可以自由地混合变量声明和语句。

在C99 BNF语法的上下文中,变量声明是declaration,声明是statementstatement表示多项内容,其中一项称为compound-statement,这是熟悉的{ ... }块。 ...部分被宽松地定义为zero or more block-items ,其中 block-item 被宽松地定义为{{1} }} either a declaration or a

问题在于定义statement(转到标签,案例标签或labeled-statement,基本上是default:语句)的方式,这是松散定义的{{1} } ...: 。正如人们可能直观地预期的那样,...: zero or more statements zero or more statements 。在or declarations之后使用;基本上会终止labeled-statement : 部分zero or more }。这会导致语法回退到statements定义,这允许下一个“语句”为labeled-statementcompound-statement

我没有调查这是否是对C99语言规范的无意忽略(实际上,C99标准中的错误),或者这是否是对编写语言语法的复杂性的务实让步。如果您不熟悉编写语法,您会注意到上述说明允许递归:statement可以匹配declaration。在过于简单化的术语 labeled-statement 中,某些类型的语法递归是简单且“明确的”,而其他类型则是复杂且“含糊不清”的。为简单起见,大多数语言工具仅处理必须通过仅查看“下一个标记”来确定性地解决任何歧义的情况。我之所以提到这一点,只是因为虽然这可能在C99规范中看起来像是一个缺陷,但可能存在令人信服的,非显而易见的原因,为什么存在...而且我没有费心去做任何关于这个问题的进一步研究以找出答案无论哪种方式。

case 1: case 2: case 3: 这并不是技术上准确的描述,而是对那些不熟悉所涉问题的人的合理近似。

修改

我给出的解决方案在“大多数”情况下工作(案例是“用法”,而不是(1) (1) s),但在一种情况下确实失败:当声明声明时,这将无效一个C99 switch,例如case这是因为在C99中“跳过”C99 VLA的声明是错误的,该声明位于跳跃发生的相同词法范围内。在这些情况下,您需要使用variable length array。在这种情况下,case 1:; void *ptrs[count]; VLA的范围在结束时case 1: { void *ptrs[count]; }结束。这比第一次出现的更复杂,因为以下是完全合法的C代码,但乍一看,人们会直觉地认为它不是:

ptrs

这会编译,并在运行时打印}

另请参阅:Wikipedia: Duffs Device

答案 1 :(得分:24)

答案 2 :(得分: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];
    }
}

答案 3 :(得分:3)

我有时会遇到这种问题。

看起来很奇怪,在错误之前添加NSLog之后,它可以正常工作

修改 在C语言中,如果不在{}之间放置每个“案例”,则无法在交换机中实例化对象

答案 4 :(得分:2)

这不是一个正确的答案,因为我不知道你为什么会看到这个错误,但比在交换机块之外移动声明更好的解决方案是在交换机块的各个案例中制作明确的范围。这通常可以解决switch语句的任何问题,因为case语句共享范围有点奇怪。

所以:

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

答案 5 :(得分:1)

我之前见过这个问题。它与C中的标签有关,也适用于用于gotos的标签。 case1:,case2:行是标签。无论出于何种原因,标签后面的第一个语句不能是声明。如果其他人没有给出好的答案,我会做更多的研究和更新。

答案 6 :(得分:0)

好吧,如果我在switch语句之前放置变量声明它可以正常工作。我猜变量声明不是Objective C中的'表达式'吗?