Classes are objects, too!
One of the things that confuses people about Objective-C, is the concept of a class method. Many people think that class methods are essentially functions with Objective-C method syntax, like class methods in C++ are. But that's not true.
So, what are class methods? Well, if I wanted to confuse everybody reading this, I'd probably say something like "class methods in Objective C are exactly the same as instance methods". While this is true from a low-level standpoint, if that were true on a higher level, we wouldn't be making the distinction.
So, what's the deal with class methods?
Well, in Objective-C, classes are actually also objects. The class of these objects is "Metaclass". Confused yet? Well, until last year's German Macoun conference, I sure was. Then Amin Negm-Awad explained it to me in his session on Objective C.
Maybe it helps to keep in mind that all method calls work the same way: The object gets asked what its class is, and the class gets asked whether it implements this method for its instances. If it doesn't, the superclass gets asked the same question, and so on, until the proper (inherited) method is found in one of the superclasses. If a method was found, it then gets called, otherwise it fails (after a short attempt to let the object provide a fallback using -forwardInvocation: and the likes).
When the designers of Objective-C added class methods to the language, they had two options:
- Reimplement all that for class methods again.
- Make a class look just like another object to the code that takes care of this.
They went with the latter. They invented a special class called "metaclass" to stand in for the typical superclass, and made the compiler automatically create an object for each class you declare.
How are class methods called?
Why did they do that? That's easy: Because that way, a subclass can override the class methods of its superclass. Yes, you heard right, if your base class has a method:
+(id) factoryMethod { MyBaseClass* newObject = [[[MyBaseClass alloc] init] autorelease]; return newObject; }
you can just replace that in your subclass with a method like
+(id) factoryMethod { MySubClass* newObject = [[[MySubClass alloc] init] autorelease]; return newObject; }
But you probably knew that. What you maybe didn't know, is that "self" in class methods points at the class object. In our first case, that means we could actually write the first +factoryMethod above as:
+(id) factoryMethod { MyBaseClass* newObject = [[[self alloc] init] autorelease]; return newObject; }
The behaviour is exactly the same in the base class, when called as myVar = [MyBaseClass factoryMethod]. But in the subclass? Well, if you call this method on the subclass, as in myVar = [MySubClass factoryMethod], the same happens as would happen in a regular instance method call: The object at the start of the square brackets is the target and ends up in "self". Just in this case, this is not an instance. It is the class object MySubClass itself.
If we don't override +factoryMethod, the method we inherited from MyBaseClass gets called. With self being MySubClass, what gets executed is equivalent to:
+(id) factoryMethod { MySubClass* newObject = [[[MySubClass alloc] init] autorelease]; return newObject; }
since self is MySubClass here. Cool, isn't it?
Throw in some introspection magic
People coming from other languages occasionally write code like the following:
+(void) showModalViewControllerForClass: (NSString*)className { NSViewController* viewController = [[[NSClassFromString(className) alloc] initWithNibName: className] autorelease]; [viewController showModal]; }
And then call it like
[MyModalViewControllerBaseClass showModalViewControllerForClass: @"MyModalViewControllerSubClass"];
Now, it's cool that Objective-C lets you convert between strings and classes at all, but after my article on defensive coding, everyone should know that it is a mistake to pass a string literal instead of defining a constant. The compiler does not know that this particular string constant should contain a valid type name, so if you have a spelling error in the string, NSClassFromString returns Nil and this'll quietly fail.
But before you go defining a constant so there's only one place where you can mis-spell this string, think harder. There already is an identifier for this class. Yep, that's right, you can just use [MyModalViewControllerSubClass class] to get the class object.
Okay, so we rewrite the method to:
+(void) showModalViewControllerForClass: (Class)theClass { NSViewController* viewController = [[[theClass alloc] initWithNibName: NSStringFromClass(theClass)] autorelease]; [viewController showModal]; }
and call it as
[MyModalViewControllerBaseClass showModalViewControllerForClass: [MyModalViewControllerSubClass class]];
right? It may be terribly verbose, but Uli has explained why it's better code.
But that's still bad code. As we learned above, every method called has a self pointer pointing to the target. In the case of class methods, this points to our class. So what we can do is rewrite this to be
+(void) presentModal { UIViewController* viewController = [[[self alloc] initWithNibName: NSStringFromClass(self)] autorelease]; [viewController showModal]; }
Of course, we also need to change our calls. Instead of calling the method on the base class, what we do instead is call it on the subclass:
[MyModalViewControllerSubClass presentModal];
And with two identifiers, one of which is a shorter, more concise method name, we do what we were doing with a string, two class names and a long method name, or, to avoid the string, two nested method calls.
Objective-C is full of such subtle goodies. So the next time you think something like "class methods are pointless", "why do I have to pass strings" etc., look at the Objective-C language documentation. Chances are that there's some neat feature you can find that, among other things, solves your problem much more easily for you.
So, why confuse us with all that "classes are objects" junk?
So far, I mainly talked about self and class methods, but there is one other thing you should take along from that whole "classes are objects" business: they really are objects. What that means is that you can store them in arrays or dictionaries, get them back out again and call methods on them. That you can send methods to them (calling class methods) and self gets set to the class is really just one useful side effect.
Have a file export plugin architecture? Load the plugin bundles and build an NSDictionary mapping each plugin's file type to the bundle's principalClass. Writing an interpreter? Build an array of your different instruction classes and instantiate them as needed... there might be hundreds of other opportunities to use classes as objects.