问题
Latest version of Objective-C and XCode (4.4).
I have a code snippet and I cannot understand why I'm able to use some lines, let me explain :
// For understanding purpose : (NSMutableArray*)_programStack
id l_topItemOnStack = [_programStack lastObject];
if([l_topItemOnStack isKindOfClass:[NSNumber class]])
{
return [l_topItemOnStack doubleValue];
}
My question : since my l_topItemOnStack
is of type id
and I didn't cast it into a NSNumber
, how am i able to use the [l_topItemOnStack doubleValue]
.
I guessed that I had to cast it to NSNumber first to access the NSNumber methods...
What am I missing here ?
回答1:
Because Objective-C is a dynamic language, the message names and their declarations are just hints for the compiler - the actual message lookup and sending occurs at runtime. So even if the compiler doesn't know your object responds to the doubleValue
message, it's still able to make your call into
return objc_msgSend(l_topItemOnStack, @selector(doubleValue));
as usually.
Furthermore, the compiler looks up all the selectors that are declared anywhere in the included headers, and tries to find the best match using the actual context - here doubleValue is a unique name - it's declared on NSNumber only, so the compiler assumes that your object is indeed an NSNumber.
If you really want to avoid this, either cast the object when you call the method or initially declare it as an NSNumber.
回答2:
id
represents any Objective-C class. It's not the same as NSObject *
, because in that case you'd be unable to use the -doubleValue
method without casting it to a NSNumber
first.
You can send any message to id
(nil
is of type id
, too) without the need to cast it to a specific class. You should be cautious, though, as an unrecognized selector will lead to a crash.
Edit:id
means: a pointer to an Objective-C object of an unknown class
If you want to understand more about the difference between id
, void *
and NSObject *
you should read this blog post: http://unixjunkie.blogspot.de/2008/03/id-vs-nsobject-vs-id.html
回答3:
The compiler simply matches the object with the selector that is visible to the translation (via #import
).
It may complain if there is an ambiguity when looking up selectors to match or the message if the signatures do not match and there are multiple selectors declared. In that case, you would either have to cast the object:
return [(NSNumber*)l_topItemOnStack doubleValue];
or assign it to a new variable:
NSNumber * number = l_topItemOnStack;
return [number doubleValue];
in order to disambiguate the type for the compiler to know the proper selector's signature (so it can, for example, set up the stack correctly).
In MRC, the compiler can actually 'assume' parameter and return types (defaults to id
) -- but that is banned in ARC.
回答4:
Firstly u checked whether topItemOnStack is type of NSNumber as NSNumber inherits NSValue
An NSValue object is a simple container for a single C or Objective-C data item. It can hold any of the scalar types such as int, float, and char, as well as pointers, structures, and object ids
topItemOnStack can now be of any type floatValue, intValue or doubleValue as per our requirement
By using isKindOfClass is not casting topItemOnStack to NSNumber but checking whether it is of type NSNumber.
isKindOfClass indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class
回答5:
Objective-C objects have runtime type information bundled in (the famous isa
pointer).
You can send any message to any object; The compiler will complain (warning) if it can not find compile-time type information to decide if the object responds to the selector (in this case doubleValue
), but I believe objects of type id
, being generic, are exempt from this rule and anything goes at compile time (somebody more knowledgeable please confirm this).
Of course, if the object pointed at by the variable of type id does not implement the corresponding method (i.e., can't respond to the message sent), an exception will be thrown. In iOS this means application crash (unless the object in question has overridden this default behavior). On OS X, not always (again, somebody more knowledgeable please confirm this).
来源:https://stackoverflow.com/questions/12173711/objective-c-understanding-iskindofclass