views:

437

answers:

5

Is there any way to discover at runtime which subclasses exist of a given class?

Edit: From the answers so far I think I need to clarify a bit more what I am trying to do. I am aware that this is not a common practice in Cocoa, and that it may come with some caveats.

I am writing a parser using the dynamic creation pattern. (See the book Cocoa Design Patterns by Buck and Yacktman, chapter 5.) Basically, the parser instance processes a stack, and instantiates objects that know how to perform certain calculations.

If I can get all the subclasses of the MYCommand class, I can, for example, provide the user with a list of available commands. Also, in the example from chapter 5, the parser has an substitution dictionary so operators like +, -, * and / can be used. (They are mapped to MYAddCommand, etc.) To me it seemed this information belonged in the MyCommand subclass, not the parser instance as it kinda defeats the idea of dynamic creation.

+13  A: 

Not directly, no. You can however get a list of all classes registered with the runtime as well as query those classes for their direct superclass. Keep in mind that this doesn't allow you to find all ancestors for the class up the inheritance tree, just the immediate superclass.

You can use objc_getClassList() to get the list of Class objects registered with the runtime. Then you can loop over that array and call [NSObject superclass] on those Class objects to get their superclass' Class object. If for some reason your classes do not use NSObject as their root class, you can use class_getSuperclass() instead.

I should mention as well that you might be thinking about your application's design incorrectly if you feel it is necessary to do this kind of discovery. Most likely there is another, more conventional way to do what you are trying to accomplish that doesn't involve introspecting on the Objective-C runtime.

Marc W
I'd say it more strongly; if you think you need to do this in production code, you are very very likely doing it wrong. Downward looking inheritance introspection is extremely uncommon and is the reason why the runtime doesn't directly support it. What are you trying to do?
bbum
@bbum I've added a description of what I am trying to do. Do you still think I am doing it very very wrong? I have a hard time of thinking of another way to do this without looking for subclasses.
Johan Kool
+4  A: 

Marc and bbum hit it on the money. This is usually not a good idea.

However, we have code on our CocoaHeads wiki that does this: http://cocoaheads.byu.edu/wiki/getting-all-subclasses

Dave DeLong
Another caveat; when doing this kind of thing, you'll end up causing classes to be +initialized in an order they may never have been initialized before. This shouldn't cause problems, but sometimes does due to dependencies in the system classes that may change across versions...
bbum
The description for the code linked specifically says that this approach doesn't call `+initialize`, and although I haven't tried it yet, seems right.
Johan Kool
+4  A: 

Rather than try to automatically register all the subclasses of MYCommand, why not split the problem in two?

First, provide API for registering a class, something like +[MYCommand registerClass:].

Then, create code in MYCommand that means any subclasses will automatically register themselves. Something like:

@implementation MYCommand
+ (void)load
{
    [MYCommand registerClass:self];
}
@end
Mike Abdullah
This looks like the right way to go indeed. Especially since the documentation mentions that a class’s `+load` method is called after all of its superclasses' `+load` methods.Many thanks to the other people providing answers too though, their answers were great too.
Johan Kool
A: 

There's code in my runtime browser project here that includes a -subclassNamesForClass: method. See the RuntimeReporter.[hm] files.

NSResponder
Thanks, that's an interesting tool to add to my belt. Though I'll go with Mike's approach for this particular case.
Johan Kool
+1  A: 

Another approach was just published by Matt Gallagher on his blog.

Johan Kool