Inside the Objective-C Runtime
Pages: 1, 2
If you want to know how many parameters a method expects, just count the colons in the selector. A selector (SEL) is a const char* (that has been uniqued) so you can use standard C string functions.
For a method with more than two parameters no methods are defined. Instead we need to invoke the underlying runtime function. It's easy. The function is objc_msgSend(), which is defined as:
id objc_msgSend(id self, SEL op, ...);
So objc_msgSend() takes an object upon which a method should be invoked (a "target"), and a selector (or action) to perform, along with a variable number of arguments.
#import <objc/objc-runtime.h>; // for objc_msgSend()
SEL fiveArgumentSelector = @selector(aMethod:that:takes:five:arguments:);
id result = objc_msgSend(myCustomInstace, fiveArgumentSelector,
arg1, arg2, arg3, arg4, arg5);
An exception is raised if you "send" a message to an object which doesn't implement the corresponding method. Note we've #imported a new header. On Mac OS X you’ll find the ObjC headers in /usr/include/objc
(on other systems, try: System/Developer/Headers/objc).
|
Related Reading
Learning Cocoa |
One of the many elegant and clever uses of dynamic binding is "delegates". Delegates are widely used in the AppKit, allowing you to effectively customize standard AppKit objects by intervening at decision points and thereby modifying behavior. Rather than registering a string of callbacks, you implement whichever delegate methods you want to catch (such as: -windowWillClose:).
Thanks to dynamic typing and dynamic binding, the implementation can be on ANY class. The cool thing here about the dynamism of ObjC is that you don't need to conform to a Protocol or otherwise implement all the delegate methods: each delegate method will be invoked only if it is implemented.
This works because we can query the runtime about which methods an object implements. Once again NSObject provides a convenient O-O interface to the runtime. Does an object implement a method? "Ask" the object:
if ([aDelegate respondsTo:someSelector]) ...
Here's the declaration:
- (BOOL)respondsToSelector:(SEL)aSelector;
Another feature of dynamic binding allows container objects (NSArray, NSDictionary, NSSet, etc.) to message their contained objects consistently, without requiring those objects to reside at a particular place in the inheritance hierarchy or adopt a formal Protocol. NSArray declares the following:
- (void)makeObjectsPerformSelector:(SEL)aSelector;
- (void)makeObjectsPerformSelector:(SEL)aSelector
withObject:(id)argument;
Which invokes -(id)performSelector:(SEL)aSelector and -(id)performSelector:(SEL)aSelector withObject:(id)object, respectively on each object contained by the NSArray. You can add another makeObjectsPerform... which takes 2 (or 3 or 4) arguments using a Category.
Categories
Categories are a feature of Objective-C that allows you to extend the functionality of ANY class. You don't need the source code of the class, no need to re-compile it either. Just declare a category, implement the method, and presto! All instances of the class or of any subclass now respond to your new methods. One usage of categories is to add convenience methods:
@implementation NSString (NSString-Extensions)
- (BOOL) containsString:(NSString *)aString;
/*" Returns YES if the receiver contains aString, NO otherwise. "*/
{ return ([self rangeOfString:aString].length != 0); }
@end
But you can add any functionality you need. Sometimes it's the best way to get something done. At other times it's not and you may want to implement "helper" functions instead. For example:
BOOL doesStringContainSubstring(NSString* string, NSString *substring)
{ return (string != nil && [string rangeOfString:substring].length != 0); }
With a Category you can add class or instance methods, but you can't add instance variables.
Another use of Categories is grouping functionality and coding responsibility in a group of developers. Putting category methods in separate source (.h and .m) files can be a useful way of organizing development of your own classes.
The MiscKit makes extensive use of categories and provides examples of good Category usage along with code you may want to use.
Overidding Behavior
Another thing categories let you do is override and customize behavior for existing methods. Although I've encountered times when this let me easily do things that would otherwise have been a lot of work, it is a feature that needs care. If you override a low-level method (a dispatch method on NSObject, for instance) this can have serious side effects. Such usage is generally NOT recommended. It's always a good idea to test your code thoroughly; overriding an object's methods in a category demands good testing.
Overriding a method in a category requires no special code, just declare and implement the method.
@implementation NSObject (ObjectOverride)
- description {
return [NSString stringWithFormat:@"<objc-object>%@</objc-object>", [self class]];
}
@end
Note, as already mentioned, this is not recommended and usually leads to unwanted side effects. In cases like this, use categories to extend classes:
@implementation NSObject (XMLDescription)
- xmlDescription {
return [NSString stringWithFormat:@"<objc-object>%@</objc-object>", [self class]];
}
@end
You're free to implement both instance and class methods in categories.
If you want more background on the Objective-C language and its features, an excellent resource is available on Apple's developer Web site at: http://developer.apple.com/techpubs/macosx/Cocoa/XObjC/XObjC.pdf
Final Thoughts
Now that you've begun your exploration of runtime, I hope you'll take a look at next week's article that digs deeper and looks at how it's implemented. Plus, you'll get a look at the runtime browser, which I think you'll find very useful. See you then!
Ezra Epstein is a long time Objective-C programmer who enjoys working on Mac OS X.
O'Reilly & Associates published Building Cocoa Applications: A Step By Step Guide by Michael Mahoney & Simson Garfinkel in May, 2002.
You can also look at the Full Description of the book.
For more information, or to order the book, click here.
Return to the Mac DevCenter.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 6 of 6.
-
Adding categories at runtime
2003-02-26 14:48:41 anonymous2 [Reply | View]
-
Adding categories at runtime
2003-03-20 17:02:19 eepstein [Reply | View]
Take a look the the - (void)openAction:(id)sender
method in the AppController.m file in the source for the RuntimeBrowser. (See http://source.prajnait.com/src_RuntimeBrowser.html for the RB home page.)
The gist of dynamically loading a bundle into the rutime is a call to NSBundle's -load method.
There are ways to directly install a single method into the runtime. One way to find out more is to study the Objective-C runtime implementation source code which is released as part of Apple's Darwin project. -
Adding categories at runtime
2003-02-26 15:15:46 eepstein [Reply | View]
Yes, this can be done. You make calls directly into the runtime itself. I'll take a look to see if I've got an example hanging about and will post it here. The "trick" is telling the runtime to re-cache the methods for the class in question. This is done when a bundle is loaded, so the esaiest way to get this effect is to load the bundle with the methods you need... after all, unless you have the code pre-compiled into some other form (other than a bundle) or wrote in-line binary instructions (not recommended and very unportable), you had to have the code compiled at some point and may as well have put it into a bundle/framework. Then you just tell the Brundle/framework loader to load the bundle in question and the methods will appear. You can also, of course, implement a category method in any file that is linked into your executable and it will be available when the program is running.
Am curious about the application of this technique, if you'd care to share.
-
dynamic dispatch cost?
2002-05-29 13:51:22 veloso [Reply | View]
How much time does dispatching take? It seems that dispatching is an expensive process - especially just for the ability to override/substitute methods at runtime and create dynamic hierarchies, etc.
I'm still learning objc/etc, so maybe I'm missing something - or I'll wait until you dig into the object runtime in the next article? -
dynamic dispatch cost?
2002-06-13 13:54:17 rthille [Reply | View]
Also note that for tight loops, you can (outside the loop) do the method-selector to function-pointer lookup once, and then inside the loop just call the function through the pointer.
Robert -
dynamic dispatch cost?
2002-05-29 14:16:24 eepstein [Reply | View]
I dig into this a little more in the next article, but wanted to reply here.
The issue of performance that is at the heart of this question was an issue that had been frequently raised in the past on a theoretical level. On a practical level, due to the way that the Obj-C runtime is implemented, a dynamic method invocation takes about 1.7x a regular function calls (i.e., less that 2 function calls).
The longer version of that answer is that this impressive performance is the result of caching and the Obj-C runtime goes to some lengths to guarantee very fast cache resolution. Basically it's not method dispatch, but method "resolution" -- or finding the correct method implementation for a method invocation -- that takes time. Since this is basically static for a running app (only changes if/when a dynamic bundle is loaded) the lookup results can be cached and you get excellent performance. There is a one-time overhead for most methods of the first lookup. (This can be short-circuited for by having the runtime pre-load caches for specific methods.)
More to the point, really, is the basic question: when is performance an issue? The rough estimate in standard use is that only 10% of executable code ever benfits from being optimized for speed. Generally with GUI apps and DB apps and apps going over a wire, the bottle neck is not in the application. Heck, you can get reasonable web performance running interpreted Perl scripts. If, on the other hand, you need a Fast Fourier Transform to run on a tiny 20 MHz processor, you'll be needing to code the routines in assembler, as even C won't be fast enough.
Finally, looking at the places I've seen C++ code try to do dynamic things I've found a consistent pattern: Objective-C is ORDERS of MAGNITUDE FASTER than C++ !!! Shocking? True! Consider: the average Motif "callback" function takes an event. It then proceeds to compare some arbitrary attribute of the event (the tag, say, of a button that was clicked) in a huge if-then-else block. First this is simply bad, brittle code (though very common). Second it throws away at the outset the essential information that the window manager busily gathered for you: which widget was clicked. Third, it is really slow. 10x slower than Target-Action in Objective-C! And more brittle. And, well, dumber. Yet it is in almost every UI App I've seen. Which makes the final point : a modern computer is so fast that you can write really bad, slow code in C++ and it will still perform ok. Of course you could also write the app in Obj-C and have elegance and speed -- believe it or not, thanks to dynamism.
In my experience, Objective-C has never been a performance bottleneck. On occaission, I've found bits of code that needed to be optimized. On even rarer occaisions I've needed to code functions in pure C, but in those occaisions, it was obvious at design-time, as the problem being solved was computationally intense and performance was a system requirement.
Finally, as you dig into Obj-C and find the power of development that comes with it (simple things that are incredibly hard, tedious or impossible in C++, like: adding methods on a class high up in the hierarchy without needing to find/recompile all subclasses, changing inheritance locations without recompile all of your code, etc. etc.) you'll find that what dynamism buys you for the 1.7x overhead, is a great deal indeed.






(and, if it is, could you please give a short example or point me / us to some information?)