Passing in a custom selector implementation

484 views Asked by At

Supposed I have two objective-c classes, LBFoo and LBBar.

In LBFoo I have a method that looks like this:

- (void)doSomethingWithFoo:(NSNumber*)anArgument
{
  if(anArgument.intValue > 2)
    [LBBar doSomethingWithLBBar];
  else
    [LBBar doSomethingElseWithLBBar];
}

What I would like to do instead is pass an implementation to LBBar that was not declared ahead of time. (As in dynamically override an existing @selector within LBBar)

I know that an IMP type exists, is it possible to pass an IMP to a class in order to change its selector implementation.

2

There are 2 answers

0
John Corbett On BEST ANSWER

you can use the method_setImplementation(Method method, IMP imp) function in objective-c runtime.

if you want to set an instance method, it would work something like this

method_setImplementation(class_getInstanceMethod([yourClass class], @selector(yourMethod)), yourIMP);

if you want a class method, just use class_getClassMethod instead of class_getInstanceMethod. The arguments should be the same.

that's all there is to it. Note that IMP is just a void function pointer with the first 2 parameters being id self and SEL _cmd

2
jscs On

You can certainly use the runtime functions to do something like this,* but I'd suggest that this is exactly the sort of problem that Blocks were introduced to solve. They allow you to pass around a chunk of executable code -- your method can actually accept a Block as an argument and run it.

Here's a SSCCE:

#import <Foundation/Foundation.h>

typedef dispatch_block_t GenericBlock;

@interface Albatross : NSObject 
- (void)slapFace:(NSNumber *)n usingFish:(GenericBlock)block;
@end

@implementation Albatross

- (void)slapFace:(NSNumber *)n usingFish:(GenericBlock)block
{
    if( [n intValue] > 2 ){
        NSLog(@"Cabbage crates coming over the briny!");
    }
    else {
        block();    // Execute the block
    }
}

@end

int main(int argc, const char * argv[])
{

    @autoreleasepool {

        Albatross * p = [Albatross new];
        [p slapFace:[NSNumber numberWithInt:3] usingFish:^{
            NSLog(@"We'd like to see the dog kennels, please.");
        }];
         [p slapFace:[NSNumber numberWithInt:1] usingFish:^{
            NSLog(@"Lemon curry?");
        }];

    }
    return 0;
}

*Note that using method_setImplementation() will change the code that's used every time that method is called in the future from anywhere -- it's a persistent change.