【问题标题】:Declaring UI Objects in Switch Cases在 Switch Case 中声明 UI 对象
【发布时间】:2015-08-02 19:36:52
【问题描述】:

我已经阅读了关于 switch 案例的范围,即 跳转标签 等等,但是这里建议的解决方案似乎暗示添加花括号可以规避这个问题。但是,这似乎仍然不起作用:

switch (objectType) {

  case label:   //label is an integer constant
    NSLog(@"statement before declaration");
    UILabel *control = [[UILabel alloc] init];       //no error
    break;

  case button:  //button is an integer constant
    {
      UIButton *control = [[UIButton alloc] init];   //no error
    }
    break;    

  default:
    break;
}

// error when trying to use the *control* variable,
// Use of undeclared identifier 'control'

有什么方法可以用 switch 语句来完成这个吗?


2015 年 5 月 23 日:尝试了许多不同的方法,但都没有成功。

编辑:实施 Cyrille 建议的解决方案时出错:

UIView *control = nil;
switch (objectType)
{
  case label:  //button is an integer constant
    {
      control = [[UILabel alloc] init];   //no error

      //error: property 'text' not found on object of type 'UIView *'
      control.text = @"Something...";     
    }
    break; 

  default:
    break;
}

显然,即使从UIView重铸为UILabel,对象也没有继承UILabel的所有属性,因此报错:

在“UIView *”类型的对象上找不到属性“文本”

即使是 Zil 建议为类型添加前缀 (UILabel*)[[UILabel alloc]init]; 也没有奏效。

有人让这个工作吗?


DUNCAN C. 的解决方案 (请参阅下面接受的答案)

    UIView *control = nil;
    switch (objectType)
    {
        case label:  
            control = [[UILabel alloc] init];
            ((UILabel *)control).text = @"Something...";     
            break; 

        default:
            break;
    }

    // UI object instantiated inside switch case recognised!
    ((UILabel *)control).textColor = [UIColor redColor];

谢谢邓肯 C。

【问题讨论】:

  • 请注意,如果您有另一种情况,即按钮,它创建一个按钮并分配control=button,则您在 switch 语句之外的代码将在运行时崩溃。那是因为在这种情况下,控件对象不包含标签,它包含一个按钮,如果你将它转换为 UILabel,你是在告诉编译器“相信我,它是一个标签”,但你错了。跨度>
  • @Duncan C:你说的完全正确;我在发布之前也注意到了这一点,但只是想简单地表明该对象确实在 switch 块之外被识别。谢谢。

标签: ios objective-c scope switch-statement declaration


【解决方案1】:

Objective-C 中的作用域规则非常简单。任何时候你输入一对花括号,你就进入了一个新的范围。在该范围内声明的任何变量仅存在于该范围内。当你退出大括号时,变量不再存在。

switch 中的各个 case 不需要大括号,因此它们不定义局部级别的范围,但整个 switch 语句确实使用大括号,因此它确实定义了局部范围范围。在 switch 语句的大括号内声明的任何变量都不存在于大括号外。

当您进入一个新级别的范围时,您可以访问在您的父范围中声明的所有内容。

你没有完全清楚你想要做什么。我THINK你想要做的是拥有一个父类的变量,然后将来自子类的新对象放入其中,并且仍然可以访问子类的独特属性。

如果您声明一个基类的变量,您可以将属于该类型的子类的任何对象分配给该变量。

如果你想访问子类的属性,通过,你必须将变量转换为适当的类型:

UIView *control = nil;
switch (objectType)
{
  case label:  
    control = [[UILabel alloc] init];   //no error

    ((UILabel *)control).text = @"Something...";     
    break; 
  case button: 
    control = [UIButton buttonWithType: UIButtonTypeSystem];
    [((UIButton *)control) setTitle: "Foo" forState: UIControlStateNormal];
  default:
    break;
}

编辑

我发现强制转换语法很难输入,而且会让人感到困惑。有时创建对象实际类型的新临时变量会更清楚,做任何你需要做的事情,然后将其分配给目标变量,如下所示:

UIView *control = nil;
switch (objectType)
{
  case label:  
    UILabel *aLabel = [[UILabel alloc] init];   //no error

    aLabel.text = @"Something...";   
    aLabel.someOtherProperty = someOtherValue;
    control = aLabel;  
    break; 
  case button: 
    UIButton aButton = [UIButton buttonWithType: UIButtonTypeSystem];
    [aButton setTitle: "Foo" forState: UIControlStateNormal];
    control = aButton
  default:
    break;
}

在 switch 语句之外,您将不知道控制变量包含哪种类型的对象,因此您将只能访问 UIView 基类的属性,除非您强制转换变量到具体的类型,如上图。

(请注意,上面的名称“控件”是一个错误的选择,因为标签不是控件。最好使用像“aView”这样的名称,它暗示可能在变量中的所有对象的公共类。)

【讨论】:

  • 哇!有用! ((UILabel *)control).text - 我自己不会想出来的。谢谢你,真诚的。你真好,应我的要求回答了我的问题。我关注了很多你的作品,我个人觉得你的解释非常清晰简洁,易于理解。你绝对应该写一本关于这个主题的书。谢谢邓肯。
  • 不用担心。查看我的编辑以了解另一种方法。
  • 这将是无价的,尽管以这种方式分配时,开关块外部的 control 对象似乎不会继承相应控件的属性 - 标签没有文字、文字颜色等,例如。另外,如果可以的话,当单独的 case 块 (即:大括号) 的单独范围被删除时,如果第一行是对象的初始化程序,Xcode 会抛出错误 - UILabel *aLabel = [[UILabel alloc] init]; .如果在第一行插入了某个函数,比如NSLog(),那么就可以了。这是为什么呢?
  • 我也发现了将新变量声明为案例的第一行的问题,但不知道是什么原因造成的。这很烦人。
  • 确实如此。不是在每个案例块的开头放置NSLog(),如果每个案例都包含在自己的大括号中,会有什么问题吗?您是否也确认了简单地将先前声明的 UIView 对象 (在 switch 块之外声明) 分配给在 switch 块中实例化的对象不会继承对象的属性?再次感谢您。
【解决方案2】:

是的,只需在切换之前声明一个通用的control

UIView *control = nil;
switch (objectType) {

  case label:   //label is an integer constant
    NSLog(@"statement before declaration");
    control = [[UILabel alloc] init];       //no error
    break;

  case button:  //button is an integer constant
    {
      control = [[UIButton alloc] init];   //no error
    }
    break; 

  case other:
    control = [[UIView alloc] init]; // works too   

  default:
    break;
}

NSLog(@"Control frame is %@", NSStringFromCGRect(control.frame));

【讨论】:

  • 谢谢。不知道 UI 元素可以以这种方式重新投射。我会试一试。
  • 不,不,我错了。 UILabel 不是 UIResponder。只需使用标准的UIViewid 类型(已编辑答案)
  • NSObject > UIResponder > UIView > UILabel vs NSObject > UIResponder > UIView > UIControl > UIButton,共同祖先是UIView
  • 不幸的是,UIControlUIView 声明似乎都不兼容。例如,当重铸为 UILabel 时,声明有效,但在尝试分配文本、textAlignment 等时会弹出 property ... not found 错误。
【解决方案3】:

一旦您将控件声明为UIView *,如果您尝试访问 UIView 上不存在的属性,您将收到错误消息。

但是,正如 Duncan C 所说,您始终可以使用点表示法转换和访问该属性,例如:

((UILabel *)control).text = @"This Works";

然后访问值

NSLog(@"Control text: %@", ((UILabel *)control).text);

我打算采用不同的方法,将您的控件声明为id 并使用Objective-C 方法调用。您需要确保控件响应您尝试调用的选择器。如果您想保护您的应用程序在尝试访问选择器时崩溃,您可以使用respondsToSelector: 方法。 我的建议的一个例子:

unsigned objectType = 1;
id control = nil;

switch (objectType) {

    case 1:   //label is an integer constant
        NSLog(@"statement before declaration");
        control = [[UILabel alloc] init];       //no error
        [control setText:@"a"];
        break;

    case 2:  //button is an integer constant
    {
        control = [[UIButton alloc] init];   //no error
    }
        break;

    default:
        break;
}

NSLog(@"Control text %@", [control text]);

结果是:

2015-05-23 13:12:06.840 test2[46589:12231454] statement before declaration
2015-05-23 13:12:06.841 test2[46589:12231454] Control text `a`

希望有帮助:)

【讨论】:

  • 使用id 类型似乎可以工作,尽管点符号似乎不适用于这样的对象 - 只有方法消息传递。谢谢。
【解决方案4】:

问题是您的 control 变量在 switch 语句的范围内声明,然后您试图在外部使用它:

UILabel *controlLabel = nil;
UIButton *controlButton = nil;

    /*
    I've defined two variables but you could do the same with one like so
    id control = nil;
    or
    UIControl *control = nil;
    */

switch (objectType) {

  case label:   //label is an integer constant
    NSLog(@"statement before declaration");
    controlLabel = [[UILabel alloc] init];       //no error
    break;

  case button:  //button is an integer constant
    {
      controlButton = [[UIButton alloc] init];   //no error
    }
    break;    

  default:
    break;
}

if (controlLabel) {

} else if (controlButton) {

}

【讨论】:

  • 这只是扼杀了拥有单个 control 变量的意义。
  • 您可以使用 id 或 UIControl 实现相同的效果,这样可能会更容易,因此您以后不必进行类型转换。
  • @RASS:是的,但是从 UIControlUIView 重铸似乎存在一些属性兼容性问题。
  • 即使在做(UILabel*)[[UILabel alloc]init]; ?
  • @Zil:是的。即使(UILabel*)[[UILabel alloc]init]; 我错过了什么吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-05
  • 1970-01-01
  • 2017-02-03
  • 2011-04-15
  • 2020-12-27
  • 1970-01-01
  • 2016-06-15
相关资源
最近更新 更多