【问题标题】:Passing NSNumber* to NSString* expected-parameter does not cause compiler warning / error将 NSNumber* 传递给 NSString* 预期参数不会导致编译器警告/错误
【发布时间】:2011-08-25 16:33:56
【问题描述】:

有什么办法可以解决这个问题吗?

我更改了函数的签名以传递 NSString 对象而不是 NSNumber 对象。除了我有一些实例仍然通过旧的 NSNumber 对象。很难追踪,因为编译器不会为此显示任何错误或警告。我尝试删除 DerivedData 文件夹。没用。

我也尝试过分析,也没有发现这些问题。

我知道我可以进行 NSAssert 检查以确保传入的参数类型正确,但这似乎是倒退的。这应该是编译器发现并警告我的东西。

有什么建议吗?

- (void) test:(NSString *)par;

打电话

NSString *str = (NSString *)[array objectAtIndex:0];

除了对象是 NSNumber,所以从技术上讲,它会将 NSNumber 转换为 NSString。调试器将其视为 NSNumber,因此不确定为什么首先允许它。

【问题讨论】:

  • 除了在需要的地方学习更具防御性的编码外,您对此无能为力。当您明确地转换为某些东西时,您必须确保它会起作用。
  • 没有我可以设置的断点来调试这些类型的错误类型吗?
  • @David - 如果你明确地对这样的指针进行类型转换,那么游戏就结束了。编译器怎么能告诉你不是意思这样做的?它不知道数组中有什么——你必须自己维护这些信息。
  • 您可以将 Xcode 设置为在 ObjC 异常上中断,例如在这种情况下,当调用无法识别的选择器时 - 就是这样。对于运行时的 ObjC,在大多数情况下,对象的类型基本上无关紧要,只要它支持在其上调用选择器即可。

标签: objective-c debugging xcode4


【解决方案1】:

有什么办法解决这个问题?

不容易。断言(正如你提到的)是一个起点。

我更改了我的函数签名以传递 NSString 对象而不是 NSNumber 对象。

objc 不能那样工作 - objc 对象只是在执行时通过它们的参数/变量传递地址。

类型转换时语言没有提供显式类型转换;如果这是您的期望。

objc 也不使用类型安全转换来确定参数是否属于它被转换为的类型。

除了我有一些实例仍然传递旧的 NSNumber 对象。很难追踪,因为编译器不会为此显示任何错误或警告。我尝试删除 DerivedData 文件夹。没用。

你会得到 NSNumbers,因为那是数组中存在的。

我也尝试过分析,也没有发现这些问题。

这是一种非常动态的语言,它就是这样设计的。

分析器无法发现或查找这些问题,因为检查必须在运行时执行。

此外,传递 objc 对象并动态使用它们是司空见惯的(使用 respondsToSelector:isKindOfClass:)。

我知道我可以进行 NSAssert 检查以确保传入的参数类型正确,但这似乎是倒退的。这应该是编译器发现并警告我的东西。

我使用断言并编写了几个检查和类型以在我想要的地方返回类型安全。

我知道这方面没有公共库 - 您可能需要自己实施所需的检查。为这些检查创建一个简单的标头并实现它们很容易。

除了对象是 NSNumber,所以从技术上讲,它会将 NSNumber 转换为 NSString。调试器将其视为 NSNumber,因此不确定为什么首先允许它。

对于 objc 变量,调试器评估地址处的对象以确定其类型,而不是使用变量声明的类型。

【讨论】:

    【解决方案2】:

    调试器将其视为正确的 (NSNumber) 类型这一事实并不意味着 编译器 也应该看到这一点。编译发生在调试之前,编译器拥有的唯一信息是静态源代码。如果你将一个指针转换为另一种类型,你实际上是在告诉编译器“忘记你认为你知道的关于这个指针的一切;它现在是 THIS 类型”。编译器无法知道在运行时实际上会有不同的类型。

    编译器会警告您有关冲突的参数类型,但为了看到该警告,类型必须在编译时实际发生冲突。强制转换强制类型在编译时匹配,因此您唯一的选择是更改或删除所有有问题的强制转换。

    【讨论】:

      【解决方案3】:

      进一步讨论:Objective-C 对象可以被视为无类型。因为运行时是完全反射的,[objTypeA someSelector][objTypeB someSelector] 都产生完全相同的编译代码。这与 C++ 等语言不同,后者需要知道对象的类型才能清楚地知道如何调用特定方法。

      另一种描述方式是Objective-C 支持duck typing。因此,您的问题的解决方案是:

      @interface NSString (stringValue)
      
      - (NSString *)stringValue { return self; }
      
      @end
      
      // ... elsewhere ...
      
      - (void)test:(id)par
      {
          NSString *stringValueOfPar = [par stringValue];
      
          // now proceed with stringValueOfPar as you previously did with par
      }
      

      您在那里所做的事情是扩大了您的测试方法的定义,因此它不是采用特定类型,而是采用在您调用 stringValue 时将自身转换为字符串的任何类型。 NSNumber 已经满足该条件,但 NSString 不满足。所以你扩展NSString。所以你创建的是一个非正式的协议。

      贾斯汀的答案是更实用和正确使用的答案,这个答案有效地部署了 Objective-C 的能力来修补你一开始就不应该犯的错误。我发布了这个答案,试图帮助解释为什么这种事情不仅在 Objective-C 中被允许,而且有时非常有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-17
        • 1970-01-01
        • 2021-12-28
        • 2018-03-25
        • 2017-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多