Passing in a custom selector implementation
-
23-06-2021 - |
Вопрос
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.
Решение
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
Другие советы
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.