有什么方法可以解决这个问题?
我更改了函数的签名以传递NSString对象而不是NSNumber对象。除了我有一些仍然传递旧NSNumber对象的实例。由于编译器没有显示任何错误或警告,因此很难跟踪。我尝试删除DerivedData文件夹。没有帮助。
我也尝试过分析,但也没有发现这些问题。
我知道我可以进行NSAssert检查以确保传入的参数类型是正确的,但这似乎是倒退的。这应该是编译器选择并警告我的东西。
有什么建议吗?
- (void) test:(NSString *)par;
调用
NSString *str = (NSString *)[array objectAtIndex:0];
除了对象是NSNumber之外,所以从技术上讲它会将NSNumber强制转换为NSString。调试器将其视为NSNumber,因此不确定为什么首先允许它。
答案 0 :(得分:2)
有什么方法可以解决这个问题吗?
不容易。断言(正如你所提到的)是一个起点。
我更改了函数的签名以传递NSString对象而不是NSNumber对象。
objc不起作用 - objc对象只是在执行时通过参数/变量传递地址。
在进行类型转换时,语言没有提供明确的类型转换;如果这是你的期望。
objc也不使用类型安全转换来确定参数是否属于它的类型。
除了我有一些仍然传递旧NSNumber对象的实例。由于编译器没有显示任何错误或警告,因此很难跟踪。我尝试删除DerivedData文件夹。没有帮助。
你得到NSNumbers,因为这就是数组中存在的东西。
我也尝试过分析,但也没有发现这些问题。
这是一种非常动态的语言,就是它的设计方式。
分析器无法接收这些问题或查找它们,因为必须在运行时执行检查。
此外,通常传递objc对象并动态使用它们(使用respondsToSelector:
和isKindOfClass:
)。
我知道我可以进行NSAssert检查以确保传入的参数类型是正确的,但这似乎是倒退的。这应该是编译器选择并警告我的东西。
我使用断言并写了几个检查和类型,以便在我想要的地方返回类型安全。
我知道没有公共资源管理系统 - 您可能需要自己实施所需的检查。为这些检查创建一个简单的标题并实现它们很容易。
除了对象是NSNumber之外,所以从技术上讲它会将NSNumber强制转换为NSString。调试器将其视为NSNumber,因此不确定为什么首先允许它。
对于objc变量,调试器评估地址处的对象以确定其类型,而不是使用变量声明的类型。
答案 1 :(得分:1)
调试器将其视为正确(NSNumber)类型的事实并不意味着编译器也应该看到它。编译在调试之前发生,编译器唯一的信息是静态源代码。如果你正在向另一个类型转换指针,那么你有效地告诉编译器“忘记你认为你对这个指针的所有知识;它现在是这种类型”。编译器无法知道运行时实际上会有不同的类型。
编译器会警告您有关冲突的参数类型,但为了查看该警告,类型必须在编译时实际冲突。强制转换迫使类型在编译时匹配,因此您唯一的选择是更改或删除所有违规的强制转换。
答案 2 :(得分:0)
进一步讨论:Objective-C对象可以被视为无类型。因为运行时是完全反射的,所以[objTypeA someSelector]
和[objTypeB someSelector]
都会产生完全相同的编译代码。这与C ++之类的语言形成对比,在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
。所以你创建的是一个非正式的协议。
Justin的答案是更实用,更合适的一个,这个有效地部署了Objective-C的能力来修补你不应该首先犯的错误。我已经发布了这个答案,试图帮助解释为什么这种事情不仅仅在Objective-C中允许,但有时非常有用。