May 6, 2009

Reflection in Objective-C

When I first learned Java, I thought the whole reflection package was novel, and it put Java closer to the realm of interpreted scripting language and farther away from the then-mainstream languages of C and C++. The idea of being able to look inward at the class metadata of an object at runtime was really fascinating, although not always terribly useful in everyday application programming. Those programmers coming over to the Objective-C world from Java will be happy to know that Objective-C also supports the concepts of reflection, AKA introspection. In fact, Objective-C 2.0 has many runtime features such as dynamically changing a class definition and even creating a new class dynamically. The necessity for these abilities is rather obscure, and it firms my belief that Objective-C is a ridiculously bloated language. To me, Objective-C has a bit of an identity crisis: it's not quite sure if it's an interpreted language or a compiled language. The runtime is mostly dynamic. Unlike C++, Objective-C does runtime binding. This is why you can do things like define methods for an implementation that are never declared in your header file, or extend existing classes with categories. Unfortunately, because of all this bloat, it is hard to find just the simple stuff that you might actually use in your day-to-day code. I will try to unearth some of these jewels and bring them to light.

Grandpa NSObject

Most, if not all, the runtime reflection support comes from the NSObject class. NSObject, like Java's Object class, is the granddaddy class of all classes (except for some rare exceptions). Therefore, all of your classes already have access to this reflection support. It's important to note that all this reflection support is not part of the Objective-C language per se; it is part of the NS* runtime environment. This is also why some of this stuff seems tacked on. It's because it has, uh, been tacked on.

Getting Class Metadata

You can access the class metadata for an object by calling the class method:

Class c = [self class];

This method is both an instance and a class method. It returns a C struct full of all sorts of mysterious stuff, like the list of instance variables, methods, etc. All of this is old hat to the java.lang.reflect package users out there, but the interfaces to reach this stuff in Objective-C seems really complex. This is probably difficult by design to keep the riff-raff out. So far, the only use I have for the class method is to provide the parameter to the isKindOfClass method described below. I never need to actually look at the contents of the Class structure.

Dynamic Method Invocation

I already discussed one aspect of reflection in my article about invoking methods. This allows you to build up a method call, arguments and all, at runtime. This is similar to using the java.lang.reflect.Method class in Java.

Testing inheritance

Java has an operator called instanceof that allows you to check if a object is an instance of a particular class or interface. Objective-C has a similar feature, the isKindOfClass: method. isKindOfClass: will return YES if the receiver is an instance of the given class or an instance of any class derived from the class. For example, if I had an array of related pointers, I can operate on each differently according to its type:

for(BaseClass* base in myArray) {
 if([base isKindOfClass:[ClassOne class]]) {
     // do stuff specific to ClassOne
 }
 else if([base isKindOfClass:[ClassTwo class]]) {
     // do stuff specific to ClassTwo
 }
 else if([base isKindOfClass:[ClassThree class]]) {
     // do stuff specific to ClassThree
 }
 // etc
}

If you want an exact class match, and not match any inheriting classes, then you use isMemberOfClass:.

Testing Protocol Conformance

Similar to class instance testing, you can also test whether an object conforms to a particular protocol. Java neatly uses instanceof for both classes and interfaces, but Objective-C has to have a more clunkier approach. To test conformance, use the conformsToProtocol: method:

BOOL conforms = [obj conformsToProtocol:@protocol(MyInterface)];

Testing for Method Existence

To an old Java and C++ hack like me, it seems extremely odd that you may not know if an object implements a method or not. But since Objective-C classes are largely dynamic, you need a way to check if a method you need is actually there. Hence the method respondsToSelector:. Here you are asking the receiver if it implements (or inherits) the given method:

if([obj respondsToSelector:@selector(aMethod:)]) {
   // it's there, so we can call it
   [obj aMethod:YES];
}

Certainly, there is way more you can do on the reflection front in Objective-C, but I tried to discuss the more commonly used reflection mechanisms. If you need too add hardcore dynamic nature to your software, you should be familliar with these documents: Runtime Programming Guide: Introduction Runtime Reference

2 comments:

  1. Wow, you really proved your case that the language is bloated. Thanks for busting a sweat!

    ReplyDelete
  2. I'm reasonably new to Objective C, so read your post with interest. However, I've found reflection to be really useful in a couple of instances. The first is in providing users with the ability to script. With reflection I can instantiate their classes, call their methods, etc. The second use is in providing cross-platform functionality at run time, i.e. creating objects that are operating system dependent. Obviously, Objective C is limited here, but are very useful when doing this kind of work in C#.

    ReplyDelete