Parmanoir

Dynamic method over* from a Javascript standpoint

In Javascript, you can easily override or overload any method of any object : MyObject.prototype.someFunction = newFunction will set a new class or instance method for all MyObject instances, MyObject.someFunction = newFunction sets a new method just for that instance. While the latter is not possible in ObjC, the former is thanks to the runtime functions, namely
  • class_addMethod to add a C function as a method to a class — we'll need to provide a signature that defines the function parameters and return values. For example, the signature of drawRect: is v24@0:4{_NSRect={_NSPoint=ff}{_NSSize=ff}}8 (get it with method_getTypeEncoding)
  • class_getInstanceMethod and class_getClassMethod to get pointers (called implementations) to class and instance methods
  • once we have the implementations of our new and old methods, use method_exchangeImplementations to exchange them

To overload a class method, we add a new method to that class and exchange their implementations. Then when our new method is called, we do our job and call the original one. To add a method, we use either class_addMethod or a class category :

@implementation ExistingClass (MyOverrideMethod)
- (void)doStuff2
{
 // do extra stuff
 …
 // call original method (this is not a recursive call as implementations have been swapped)
 [self doStuff2]
}

@end

Be careful before overloading a method. If you overload NSButton's drawRect with a new method drawRect2:, and have a slider in your interface, you'll crash in [NSSlider drawRect:]no such method drawRect2. Why ? NSButton does not have its own drawRect method : its drawing is done by its parent class, NSControl. method_exchangeImplementations will happily swap a parent class method (NSControl's drawRect) with a derived class method (NSButton's drawRect2), guaranteeing a crash in sibling classes (NSSlider, NSTextField). To overload NSButton's drawRect, no need to jump through hoops : adding a drawRect: method to NSButton with a category and calling [super drawRect:…] will work just fine ! :)

So before overloading a method, compare its implementation with the superclass implementation (get the superclass with class_getSuperclass, then compare the results of class_getInstanceMethod). If they differ, use the method_exchangeImplementations technique. If not, overload with a category method.

Objective-C 2.0 Runtime Reference
Dynamic overloading : sample code

Objective-C from Javascript

Karsten
2008 04 28

wouldn't it be simpler to just use MethodSwizzling for that? Jonathan Rentzsch did a nice project to have a clean implementation of it. Michael Tsai bloged about it here: http://mjtsai.com/blog/2007/12/29/jrswizzle/

Karsten

Patrick Geiller
2008 04 28

Hi Karsten,

Ah yes, jrswizzle copies the parent implementation at the derived class level with class_addMethod. So it is simpler ! Thanks for the link.


Follow me on Twitter
Planet Cocoa
Cocoa.fr

2011 02 22Distance field
2010 07 202Binding through NSApp
2010 05 122Forwarding invocations
2010 02 272Core Image black fringes
2010 02 21Quickest Way to Shell
2010 02 08Who's calling ?
2009 09 2138 ways to use Blocks in Snow Leopard
2009 08 182Bracket Mess
2009 08 124Taming JavascriptCore within and without WebView
2009 04 15Debugging with Activity Monitor
2009 03 25How Core Image Color Tracking works
2009 03 1510Custom NSThemeFrame
2009 03 10Which framework is running ?
2009 03 074CoreUI can paint pretty big
2009 02 18Localization with functions
2009 01 30Did you forget to nest alloc and init?
2009 01 16JSCocoa on the iPhone
2009 01 11Mixing WebView and JavascriptCore
2009 01 09Badge overflow
2009 01 09Find your Garbage Collection leaks with Instruments

Powered by MediaWiki