Objective-C for C++ Programmers, Part 3
In part 1 and part 2 of this series, we looked at the core of the Objective-C language, defining classes, creating objects, and sending messages to them. This final article covers the more advanced parts of the language.
Introspection
C++ has some support for introspection via Runtime Type Information (RTTI). This is generally poorly supported across compilers, though; even where it's supported, it typically isn't used, "for performance reasons." In contrast, introspection information is always available in Objective-C.
The class structure typically contains a linked list or array of metadata about methods and another about instance variables. For each instance variable it includes the offset from the start of the object, the type, and the name. You can use this setup to do some quite interesting things. For example, I've written a framework for Étoilé that inspects this information and uses it to serialize objects automatically (only possible in C++ for objects for which you have the source code). For methods, it has the name, the types, and a pointer to the function used to implement the method. This is an Instance Method Pointer (IMP), and is defined in a header as follows:
typedef id (*IMP)(id, SEL, ...);
This uses two other Objective-C types. The id type is a pointer to some kind of object. All you know about an id is that it will respond to messages (although you don't know which messages, unless you ask it). The other is SEL, the type of a selector.
You can do this:
IMP method = [object methodForSelector:@selector(doSomething)];
The variable method is now a C function pointer to the implementation of the method. If you're sending the same message to an object, you can use this capability for some extra speed.
Since you can construct selectors at runtime, you can use this approach for some very dynamic behavior. In the XML parser in Étoilé, I use this when adding child elements to an object encapsulating an XML element. The root class implements an -addChild:forKey: method containing the following code:
NSString * childSelectorName = [NSString stringWithFormat:@"add%@:", aKey]; SEL childSelector = NSSelectorFromString(childSelectorName); if([self respondsToSelector:childSelector]) { [self performSelector:childSelector withObject:aChild]; }
This constructs a selector from the key name and then calls it with the object. This technique is used a lot in the XMPP implementation. For example, a <name> tag might be parsed by a class that just collects the character data and turns it into a string. When it gets to </name>, it does something like this:
[parent addChild:string forKey:@"name"];
The parent then runs this method, which will call its -addname: method with the string as an argument. A more complex form is found in Cocoa in the form of key-value coding, which introspects both method and instance variable metadata. When you call -setValue:forKey: it will call a setter, set an instance variable directly, or call -setValue:forUndefinedKey:. Using key-value coding is slower than setting instance variables directly or even calling set/get methods directly, but it completely isolates the implementation from the interface.
With Objective-C 2.0, Apple introduced properties, accessed this way:
object.property = 12;
These are a very thin layer of syntactic sugar. Internally, they're translated to set and get messages depending on how the property is used. The best feature of properties is that they allow these set and get methods to be generated automatically for any given instance variable, with retain, assign, or copy semantics.