Re: Abstracting a group of commonly named Selectors

Sandor Szatmari


Works great!  See my implementation below…
The one thing I wasn’t sure about was the types array argument for class_addMethod() The fourth param…
I used ‘i’ to specify the return type of integer.  The return type is an enum.  Wasn’t sure if there was a better choice
This removed a ton of unnecessarily redundant code! 

self = [super init];
if ( self != nil )
IMP parser_imp = class_getMethodImplementation([self class], @selector(parseStep) );
for ( SelectorStep *step in [self parseSteps] )
SEL sel = step.selector;
if ( [self respondsToSelector:sel] == NO )
class_addMethod( [self class], sel, parser_imp, "i@:" );
return self;

// --------------------------------------------

const char *selName = sel_getName(_cmd);
NSString *table = [NSString stringWithCString:selName
if ( [table isEqualToString:@"parseRoutePartitions"] )
NSLog( @"Stop" );
NSAssert1( [table hasPrefix:@"parse"], @"Selector must start with parse. Bad Selector: '%@'", table );
table = [[table substringFromIndex:5] snakeCase];
return [self parseStepByNameWithParams:[self stepDictForTableName:table]];


On Jul 21, 2020, at 19:56, Sandor Szatmari via <> wrote:


So what you’re suggesting is…

1. Define a template implementation
2. Associate each selector pattern with that implementation

That seems like it should work.  I’ll give it a try and let you know how it goes…


On Jul 21, 2020, at 17:10, Jeff Laing <jefflaing@...> wrote:

In the designated initialise of your Parser class, you can use class_addMethod to add whatever you like to your instance's class.

The method implementation gets called with the selector as its second argument and you can cut the string up at runtime to then call your Parse. Here's a snippet from an app I did in the past that wanted to dynamically adjust to any entries that were added to a popup menu in Interface Builder. It iterates over the popup, and for every entry present, looks for then adds a dynamic method called _popup.

        IMP popup_imp = class_getMethodImplementation([self class], @selector(_popup00:));
        for (NSArray *item in popup) {
                // make sure that we implement the required method
                SEL sel = NSSelectorFromString([NSString stringWithFormat:@"_popup%02ld:",(long)[menuItems count]]);
                if (![self respondsToSelector:sel]) {
                        class_addMethod([self class], sel, popup_imp, "v@:@");  // -(void)method:(id)argument;

The implementation of the method gets the name of the selector using set_getName, then punts across to the actual code.

- (void)_popup00:(id)sender {
//          01234567
        const char *name = sel_getName(_cmd);
        const int idx = atoi(name+6);
        id resp = [self.popupResponders objectAtIndex:idx];
        [resp callWithArrayOfArgs:self.popupArguments];

Extracting your specific token from the message is just name+. This example is using (void) but it should work just fine - you aren't using the "no such method" path, you are just binding the method really late in the compile/link/run process...

Join to automatically receive all group messages.