最新版本的Objective-C和XCode(4.4)。
我有一个代码段,我无法理解为什么我能够使用某些行,让我解释一下:
// For understanding purpose : (NSMutableArray*)_programStack
id l_topItemOnStack = [_programStack lastObject];
if([l_topItemOnStack isKindOfClass:[NSNumber class]])
{
return [l_topItemOnStack doubleValue];
}
我的问题:由于我的l_topItemOnStack
类型为id
,而我没有将其投放到NSNumber
,我如何才能使用[l_topItemOnStack doubleValue]
。< / p>
我猜我必须首先将其转换为NSNumber来访问NSNumber方法......
我在这里缺少什么?
答案 0 :(得分:7)
因为Objective-C是一种动态语言,所以消息名称及其声明只是编译器的提示 - 实际的消息查找和发送在运行时发生。因此,即使编译器不知道您的对象响应doubleValue
消息,它仍然可以将您的呼叫转换为
return objc_msgSend(l_topItemOnStack, @selector(doubleValue));
通常。
此外,编译器查找在包含的头中任何地方声明的所有选择器,并尝试使用实际上下文找到最佳匹配 - 这里doubleValue是唯一的名称 - 它仅在NSNumber上声明,因此编译器< em>假设您的对象确实是NSNumber。
如果你真的想避免这种情况,可以在调用方法时强制转换对象,或者最初将其声明为NSNumber。
答案 1 :(得分:3)
id
代表任何Objective-C类。它与NSObject *
不同,因为在这种情况下,如果不首先将-doubleValue
投射到NSNumber
,您将无法使用id
方法。
您可以向nil
发送任何消息(id
类型为id
),而无需将其投射到特定类。不过,你应该小心谨慎,因为一个无法识别的选择器会导致崩溃。
修改强>
id
表示:指向未知类的Objective-C对象的指针
如果您想详细了解void *
,NSObject *
和{{1}}之间的区别,请阅读以下博文:http://unixjunkie.blogspot.de/2008/03/id-vs-nsobject-vs-id.html
答案 2 :(得分:3)
编译器只是将对象与翻译可见的选择器匹配(通过#import
)。
如果在查找选择器匹配时存在歧义,或者如果签名不匹配并且声明了多个选择器,则可能会抱怨。在这种情况下,您要么必须转换对象:
return [(NSNumber*)l_topItemOnStack doubleValue];
或将其分配给新变量:
NSNumber * number = l_topItemOnStack;
return [number doubleValue];
为了消除编译器的类型,以便知道正确的选择器的签名(例如,它可以正确设置堆栈)。
在MRC中,编译器实际上可以假设&#39;参数和返回类型(默认为id
) - 但在ARC中禁止使用。
答案 3 :(得分:3)
首先,检查 topItemOnStack是否为NSNumber 的类型 NSNumber继承NSValue
NSValue对象是单个C或Objective-C数据项的简单容器。它可以包含任何标量类型,如int,float和char,以及指针,结构和对象ID
topItemOnStack can now be of any type floatValue, intValue or doubleValue as per our requirement
使用isKindOfClass不会将topItemOnStack转换为NSNumber,而是检查它是否为NSNumber类型。
isKindOfClass 表示接收者是给定类的实例还是从该类继承的任何类的实例
答案 4 :(得分:1)
Objective-C对象具有捆绑在(着名的isa
指针)中的运行时类型信息。
您可以向任何对象发送任何消息;如果编译器无法找到编译时类型信息来判断对象是否响应选择器(在本例中为doubleValue
),编译器会抱怨(警告),但我相信id
类型的对象,是通用,免于此规则,任何事情都在编译时进行(有人更有见识,请确认)。
当然,如果id类型的变量指向的对象没有实现相应的方法(即,无法响应发送的消息),则会抛出异常。在iOS中,这意味着应用程序崩溃(除非有问题的对象已覆盖此默认行为)。在OS X上,并不总是(再次,有人更有见识,请确认)。