Objective-C understanding isKindOfClass
Asked Answered
B

5

2

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 ?

Bodyguard answered 29/8, 2012 at 8:27 Comment(0)
E
7

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.

Evanston answered 29/8, 2012 at 8:33 Comment(2)
I knew that message lookup occured at runtime, but XCode gave me autocompletion to write "DoubleValue" and I didn't understand how it did to provide me this ! Thanks for your answer !Bodyguard
@AndyM no problem. It's a little hidden detail which is rather weird (I wouldn't even think a compiler does this kind of unelegant trickery). Btw this might not be the reason Xcode gives you autocomplete suggestions - Xcode is not the compiler.Evanston
B
3

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

Boxboard answered 29/8, 2012 at 8:32 Comment(2)
I forgot the detail about the "id". Since I'm starting with the language (as you probably guessed :)), I'll give it a read to your blog, thanks for your answer !Bodyguard
Interesting post indead ! Thank you !Bodyguard
L
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.

Luella answered 29/8, 2012 at 8:33 Comment(0)
B
3

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

Boeke answered 29/8, 2012 at 8:35 Comment(1)
I understand your answer but that was not my question, actually I wanted to know why I had the possibility to send a message to "doubleValue" without casting my topItemOnStack to NSNumber. Thanks anyway :)Bodyguard
H
1

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).

Horsley answered 29/8, 2012 at 8:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.