25

Another question of mine about optimizing Objective C programs inspired the following: does anyone have a short example using SEL and IMP when theMethod has two (or more) integers for input?

Community
  • 1
  • 1
Ken
  • 30,811
  • 34
  • 116
  • 155

3 Answers3

50

Here's a good tutorial for getting the current IMP (with an overview of IMPs). A very basic example of IMPs and SELs is:

- (void)methodWithInt:(int)firstInt andInt:(int)secondInt { NSLog(@"%d", firstInt + secondInt); }

SEL theSelector = @selector(methodWithInt:andInt:);
IMP theImplementation = [self methodForSelector:theSelector]; 
//note that if the method doesn't return void, you have to explicitly typecast the IMP, e.g. int(* foo)(id, SEL, int, int) = ...

You could then invoke the IMP like so:

theImplementation(self, theSelector, 3, 5);

There's usually no reason to need IMPs unless you're doing serious voodoo--is there something specific you want to do?

shosti
  • 7,332
  • 4
  • 37
  • 42
  • 3
    @eman: you need to cast that return so: void (* theImplementation)(id,SEL,int,int) = (void (*)(id,SEL,int,int))[self methodForSelector:theSelector]; – Jason Coco Apr 16 '10 at 02:49
  • 1
    @Jason Coco-Correct me if I'm wrong, but I think IMP is typedef'd as (id, SEL, ...), so it shouldn't matter (although it's not typesafe, but if you need typesafety, you probably shouldn't be using IMPs). – shosti Apr 16 '10 at 02:59
  • 3
    You are right, since this method is void it is ok, but you will get errors in many other instances. You /need/ to cast was probably too strong, you /should always cast/ is probably more correct ;)--but given this question itself, my guess is that the OP probably /should not/ be using IMP either. – Jason Coco Apr 16 '10 at 03:15
  • @Jason Coco: Good point, didn't think of that (updated example). – shosti Apr 16 '10 at 03:28
  • Many thanks for the replies. You're absolutely correct that I definitely do not need this for my code but I was curious. I also thought it may be of interest more generally as I couldn't get the syntax right for more than one input. – Ken Apr 17 '10 at 03:01
  • There are other subtleties that make it important to cast the IMP to the right function type before calling it. For instance, the C calling convention for vararg function requires that `float`s be promoted to `double`s, so if you pass a `float` to an uncasted IMP that expects a `float`, it will be promoted to a `double` and you'll get incorrect results/crashes. – zneak Aug 26 '12 at 15:30
  • What is the purpose/benefits of SEL/IMP? – name-it Dec 26 '12 at 15:13
  • This was crashing for me when the selector was return type void. To get it to work I had to lose `IMP` and use a literal function pointer `(void (*myFunc)(id, SEL, ...)) = (void (*)(id, SEL, ...))[self methodForSelector:theSelector]` – Hari Honor Jun 11 '13 at 13:12
  • @shosti, from the Apple docs, if you want to send one particular message to an object (or a group of similar objects, say in an array) many times, this speeds up the process, because it avoids having to lookup the message address. – SexyBeast Oct 15 '14 at 21:06
  • Hi, I think this answer may be outdated. Because "theImplementation(self, theSelector, 3, 5);" throws a compiler error for me. – joelliusp May 06 '17 at 15:48
2

Here is another possible alternative. This avoids the crash but stubbing doesn't work.

- (void)setUp
{
   [super setUp];

   [self addSelector@selector(firstName) toClass:[User class]];
   [self addSelector@selector(lastName) toClass:[User class]];
}

- (void)addSelector:(SEL)selector toClass:(Class)class
{
    NSString *uniqueName = [NSString stringWithFormat:@"%@-%@", NSStringFromClass(class), NSStringFromSelector(selector)];
    SEL sel = sel_registerName([uniqueName UTF8String]);
    IMP theImplementation = [class methodForSelector:sel];
    class_addMethod(class, selector, theImplementation, "v@:@");
}
aryaxt
  • 76,198
  • 92
  • 293
  • 442
2

Now that I have this working thanks to eman, I can add yet another example:

SEL cardSelector=@selector(getRankOf:::::::);

IMP rankingMethod=[eval methodForSelector:cardSelector];

rankingMethod(eval, cardSelector, 0, 1, 2, 3, 4, 5, 6);

I don't need this for anything useful, I just needed to satisfy my curiosity! Thank you again.

Ken
  • 30,811
  • 34
  • 116
  • 155
  • Another good link on optimising Objective-C is http://www.mulle-kybernetik.com/artikel/Optimization/opti-3-imp-deluxe.html – Ken Apr 22 '10 at 06:41