Add Methods at Runtime in Objective-C

Andy Finnel posted some ideas for Objective-C features beyond those slated for Leopard. One of them is adding methods at runtime. It turns out this has been possible in Mac OS X since day one, it's just not obvious how to do so.

Here's what Andy says:

My previous post about Key-Value Coding and being able dynamically add transient attributes to NSManagedObject got me thinking. Why can’t I dynamically add methods? I don’t mean on just NSManagedObjects, I mean on NSObjects. I realize that categories allow that somewhat, but only as a group of functions and only at compile time.


First thing to clear up here is that categories can be loaded at runtime. Quoth the Bundle Loading documentation:

The executable code files in loadable bundles hold class (and category) definitions that the NSBundle object can dynamically load while the application runs.


But, come on, that's the easy way out. Let's dig into the Objective-C runtime. There are two structures we need to deal with:

struct objc_method {
  SEL method_name;
  char *method_types;
  IMP method_imp;
};

struct objc_method_list {
  struct objc_method_list *obsolete;
  int method_count;
  struct objc_method method_list[1];
};


This looks kinda scary, but it's actually not too bad. The steps are basically:

0. Make a function
1. Create an objc_method instance
2. Register the function name
3. Give the objc_method a pointer to the function
4. Add the objc_method to a objc_method_list
5. Pass the objc_method_list to class_addMethods
6. There is no step 6!


So here's all the code to do that:

#import <objc/objc-class.h>

// create a class with no methods
@interface EmptyClass : NSObject { }
@end

@implementation EmptyClass
@end

// define the function to add as a method
id sayHello ( id self, SEL _cmd,... )
{
  NSLog (@"Hello");
}

void addMethod ()
{
  // create the method

  struct objc_method myMethod;
  myMethod.method_name = sel_registerName("sayHello");
  myMethod.method_imp  = sayHello;
  
  // build the method list.
  // this memory needs to stick around as long as the
  // methods belong to the class.

  struct objc_method_list * myMethodList;
  myMethodList = malloc (sizeof(struct objc_method_list));
  myMethodList->method_count = 1;
  myMethodList->method_list[0] = myMethod;
  
  // add method to the class
  class_addMethods ( [EmptyClass class], myMethodList );
  
  // try it out
  EmptyClass * instance = [[EmptyClass alloc] init];
  [instance sayHello];
  [instance release];
}


Now let me be clear that I don't think is fun or easy, but it is possible. It also wouldn't be too hard to create some sort of wrapper API to do this. In any case, there are only about seven lines of code that involve actually adding the method.

Also, please don't use the above in the production code. It's a proof of concept only. It uses local variables, imperfect error checking, and so on. If you need real examples, check out the source code for PyObjC.
Design Element
Add Methods at Runtime in Objective-C
Posted Oct 26, 2006 — 23 comments below




 

ssp — Oct 26, 06 2163

To which extent would doing this actually be useful rather than just geek-cool? Any good examples for that?

Chris — Oct 26, 06 2165

Quickies:

- it's very useful for bridging dynamic languages like Ruby and Python to Objective-C. The translation layers for these languages need to add methods on demand.

- it's useful for creating Mock Objects that stand in for other objects in test-driven development methodologies.

Marco — Oct 26, 06 2166

PyObjC, for example, makes use of libffi

Rob In der Maur — Oct 26, 06 2168

This could be helpful in tweaking 'closed' (that is, not exposed through a public API) functionality; I once fiddled around using this concept to override the Print Book functionality that Apple has put into iPhoto (e.g. similar to what MyPublisher is doing). I know this is tricky, especially with 'closed' API's but sometimes it is the only way to extend or modify the functionality Apple has provided us with...

Scott Stevenson — Oct 26, 06 2170 Scotty the Leopard

To which extent would doing this actually be useful rather than just geek-cool?

Andy has some examples in his post, but this also shows you how categories can load at runtime.

Jon Hess — Oct 26, 06 2174

You could build a class dynamically at runtime, that subclasses some know class. Then add methods to it to override methods from the baseclass, then swtich a sepcific instance of the base class to your new derived class.

This may sound like a hack, but it is a much more appropriate hack than the sledgehammer that was poseAs:. Its also much more appropriate than smashing a method with a category.

Jon Hess — Oct 26, 06 2175

Err, when I said "some known class" that isn't really what I meant. I meant, if you want to specialize the behaviour of some specific object(s), but not all objects, and you only know their partial type. By using the methods outlined in my comment, and this post, you could dynamically override the method of the unknown object.

This is much cleaner than poseAs:. For example, if you posed as window, and did something special in sendEvent: (Let's not forget this would be a hack), your sendEvent: might not get invoked for every event for some subclasses of window. If you dynamically created a class, added a sendEvent: method, and swtiched the window you were interested in to be a member of your class, you could intercept *all* of the events heading to that window. This is much more correct, and percise than poseAs:.

Frederik Seiffert — Oct 26, 06 2176

Adding methods at runtime is also useful for adding Aspect-Oriented Software Development capabilities to Objective-C.
AFAIK, methods like the above have been used in AspectCocoa.

M. Vock — Oct 27, 06 2177

What would be the gcc command on the command line to get NSLog say "Hello"?

Scott Stevenson — Oct 27, 06 2185 Scotty the Leopard

What would be the gcc command on the command line to get NSLog say "Hello"?

This isn't a complete program. You'd need to add the above code to a Cocoa project.

Web design company — Dec 29, 09 7025

Thanks for the greatest guidelines.

Essay writing — Jan 06, 10 7077

Great tips. Thanks!
Buy essay papers

elik kap — Jan 10, 10 7090

çelik kapı modeli ya da
çelik kapı aksesuarından, çelik kapı müşterisi de herhangi
bir tüketiciden daha fazlası olduğunun bilincindeyiz.
Çelik kapı, taşınmaz sınıfına giren demirbaş sayılan ayrıcalıklı bir üründür.

Bob — Jan 22, 10 7140

You are great. I wish I could add something special but I cannot do it yet. As for me, I'm just a beginner. Now I 'm only taking. It has always been my dream to learn programming for a long time. This summer I advanced into actions, found numerous books at the pdf search engine http://pdf.rapid4me.com and start to work at it hard.

Bobb — Jan 22, 10 7141

You are great. I wish I could add something special but I cannot do it yet. As for me, I'm just a beginner. Now I 'm only taking. It has always been my dream to learn programming for a long time. This summer I advanced into actions, found numerous books at the pdf search engine http://pdf.rapid4me.com and start to work at it hard.

Ortan — Jan 23, 10 7148

If you dynamically created a class, added a sendEvent: method, and swtiched the window you were interested in to be a member of your class, you could intercept *all* of the events heading to that window.
Online school online post graduate course certificate online computer science degree

chalsi — Jan 23, 10 7149

I cannot do it yet. As for me, I'm just a beginner. Now I 'm only taking. It has always been my dream to learn programming for a long time.
online corrections degree online crime scene investigation degree

IP PBX — Jan 28, 10 7258

When I run into these complicated problems I usually higher programmers. I'm not at all what you would call technically savy. I've tried to program, and edit scripts. It never works. I've been reading all day and can't seem to understand a lick of what's being said.

Adam — Jan 28, 10 7262

your blog design is marvelous
<a href="http://anxietyattacksymptoms2.blogspot.com/">anxiety attacks</a> <a href="http://steamcleanersblog.blogspot.com/">cleaner</a>

Adam — Jan 28, 10 7263

your blog design is marvelous
anxiety attacks

Buyer — Jan 31, 10 7289

If you dynamically created a class, added a sendEvent: method, and swtiched the window you were interested in to be a member of your class, you could intercept *all* of the events heading to that window.

Secvut — Feb 04, 10 7348

If you dynamically created a class, added a sendEvent: method, and swtiched the window you were interested in to be a member of your class, you could intercept *all* of the events heading to that window. csi: las vegas season 10 episode 13 | bones season 5 episode 14 | community season 1 episode 15 | parks and recreation season 2 episode 15 | the office season 6 episode 15

Mervin — Feb 05, 10 7358

i will defi. try this for sure...
___________
Best sites ever Online Casino sites




 

Add Your Thoughts

Use UBB tags: [b] [i] [url] [code], and so on
Use [i] for quoting: [i]Quoted Text[/i]
Omit double quotes in links: [url=http://apple.com]Apple[/url]
Accented characters are currently stripped from names



Please enter the company who makes the Mac:
this is an anti-spam measure
Some comments may be edited for formatting, or removed if too far off-topic.




Technorati Profile
Copyright © Scott Stevenson 2004-2008