In Objective-C, given an id, how can I tell what type of object it points to?

Objective C

Objective C Problem Overview


Objective-C newbie question. Given the following (fictional) code:

id mysteryObject = [anotherObject mysteriousMethod];

How can I determine at runtime what class mysteryObject is?

Objective C Solutions


Solution 1 - Objective C

You can use isKindOfClass or isMemberOfClass

For example:

if ([foo isMemberOfClass:[NSBar class]])

Solution 2 - Objective C

[mysteryObject class]

will get you the class object. However, generally you want to do something OOPy like check for conformance to some protocol or interface.

Solution 3 - Objective C

In a dynamically typed language like Objective-C (or Python or Ruby), you often don't want to know what type of object it is. It's often more productive to think of whether the object responds to the message you wish to send; if it does, you shouldn't care what class it instantiates and if it doesn't you must handle the case regardless of the instance's type. This is known as "duck typing"...if it quacks like a duck it is a duck.

You can test whether an object responds to a particular message (known as a selector in Objective-C) like this:

if([mysteryInstance respondsToSelector:@selector(messageIWishToSend)]) {
  [mysteryInstance messageIWishToSend];
} else {
  //handle case where instance doesn't respond to the desired message
}

Even better than testing for individual selectors is to define a @protocol that describes the API you wish to use for your classes:

// MyProtocol.h
@protocol MyProtocol
- (void)methodInMyProtocol;
@end

//MyClass.h

#import "MyProtocol.h"

@interface MyClass <MyProtocol> {

}
- (void)methodInMyProtocol;
@end

You can test whether an instance implements the MyProtocol protocol like this:

if([mysteryInstance conformsToProtocol:@protocol(MyProtocol)]) {
  [mysteryInstance methodInMyProtocol];
} else {
  // ...
}

This way of doing things is often uncomfortable for folks coming from statically typed languages like Java or C++. You loose the compiler checking types for you. Dynamic typing makes a great many things easier however, including testing since you can easily replace an instance with a fake at test time. Thus the dynamic language approach is to test more and worry about types less. You do have good unit test coverage, don't you?

If you really must determine the class of an instance at run time (and you really probably don't need to), you can use -[NSObject isKindOfClass:] to test whether an instance is an instance of a class or any of its subclasses or -[NSObject isMemberOfClass:] to test whether an instance is an instance of a particular class. You can inspect the Class object directly as the return of -[NSObject class] and you can get the string name of the instance's class with NSStringFromClass([mysteryInstance class]).

Solution 4 - Objective C

I found I had to cast back to id when used with a method defined in a @protocol.

For example, self.listeners is an array of id

If I do this ....

for(id<PropertyListener> listener in self.listeners) {        
    if ( [ [ listener class]  respondsToSelector:@selector(propertyChanged:propertyName:)]) {

I get an error "No known instance method for selector 'class'". Yet, when I cast the id from id to id it works ... Why I do not understand.

[ ((id)listener) class] respondsToSelector .... 

Here is the full loop ...

for(id<PropertyListener> listener in self.listeners) {        
    if ( [ [ ((id)listener) class]  respondsToSelector:@selector(propertyChanged:propertyName:)]) {
        [listener propertyChanged: self propertyName:@"thePropName"];
    } else {
        [listener propertyChanged: self];
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJusticleView Question on Stackoverflow
Solution 1 - Objective CRoland RabienView Answer on Stackoverflow
Solution 2 - Objective Cp00yaView Answer on Stackoverflow
Solution 3 - Objective CBarry WarkView Answer on Stackoverflow
Solution 4 - Objective CBryanView Answer on Stackoverflow