72

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

id mysteryObject = [anotherObject mysteriousMethod];

How can I determine at runtime what class mysteryObject is?

Justicle
  • 14,761
  • 17
  • 70
  • 94

4 Answers4

99

You can use isKindOfClass or isMemberOfClass

For example:

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

Roland Rabien
  • 8,750
  • 7
  • 50
  • 67
  • Here a link the documentation for these methods if you need a refresher on which one detects what (I did...) http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html – Adam Ritenauer Aug 27 '11 at 21:47
  • 19
    `isKindOfClass` means the object inherits from the class, `isMemberOfClass` means exactly the same class – bendytree Mar 17 '12 at 02:20
  • 3
    To be a little bit more precise @bendytree, `isKindOfClass` means exactly the same class while also meaning the object inherits from the class – kadam Jan 24 '13 at 18:45
89
[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.

p00ya
  • 3,659
  • 19
  • 17
54

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

Barry Wark
  • 107,306
  • 24
  • 181
  • 206
  • Thanks Barry, its just for "printf" style debugging atm - I'm genuinely curious as to what classes are getting emitted by mysteriousMethod. – Justicle Jul 14 '09 at 05:17
  • You can use 'po NSClassFromString([myInstance class])' in gdb or just 'po [myInstance class]'. – Barry Wark Jul 14 '09 at 16:50
  • Testing for selectors is better than testing for protocol, because the latter forces classes to explicitly implement the protocol to work – user102008 May 25 '12 at 00:41
  • Well explained, thanks. It improved some code I had that was checking class and now more correctly checks selectors. – mm2001 Jun 03 '12 at 00:01
1

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];
    }
}
Bryan
  • 1,103
  • 12
  • 16
  • Two points: 1. Generally, you want your protocols to inherit from the `NSObject` *protocol* (yes, it's different, though related, to the class). See [here](http://stackoverflow.com/questions/679822). This will solve your problems with the `-class` method. 2. I'm guessing you are probably wanting to know whether the class _instance_ responds to the selector, e.g. `[myObject respondsToSelector:sel]`, rather than the class itself `[[myObject class] responds...]`. – Clay Bridges May 23 '14 at 09:57