tags:

views:

2175

answers:

3

I'm trying to build a NSArray of methods in Objective-C.

(What I'm trying to accomplish here is something like the following in C)

typedef (void)(*handler)(int command);

void handleCommandA(void) { ... }
void handleCommandB(void) { ... }

static const handler handler_table[10] = {
    handleCommandA, handleCommandB, handleCommandC
};

I have to port this to Objective-C and I don't know how to build an array of function pointers (in Objective-c world, class methods) at compile-time.

In Objective-C I have the following.

- (void)handleCommandA { ... }
- (void)handleCommandB { ... }

/* Now how to add above 2 functions into NSArray? */
NSArray *handler_table = [NSArray arrayWithObjects:... ];  /* This doesn't seem to work. */
+6  A: 

The problem here is that to bind those functions you must use the selector keyword which returns a SEL type. This is a pointer type whereas NSArray stores objects.

You thus have three options;

  1. Use a regular C-type array
  2. Fold the functions into an NSObject derived class that will call them.
  3. Use a protocol.

The second is likely the nicer and for this you can use the NSValue class to hold the selector results. E.g;

NSValue* selCommandA = [NSValue valueWithPointer:@selector(handleCommandA:)];
NSValue* selCommandB = [NSValue valueWithPointer:@selector(handleCommandB:)];

NSArray *handler_table = [NSArray arrayWithObjects:selCommandA, selCommandB, nil ];

When you have retrieved the correct entry from the array, to convert back you would do;

SEL mySelector = [selCommand pointerValue];
[someObject mySelector];

(Note I'm assuming that from your objective-c syntax that these are intended to be used as methods on an object and not global functions. If you wish to use them globally then you should write them as you would in plain C.)

Another option is to formalize the command methods into a protocol. This allows you to write functionality that will work on any object which implements that protocol and the compiler will provide more checking than if you were just calling selectors.

E.g.

// some header
@protocol CommandHandler
@required
-(void) handleCommandA;
-(void) handleCommandB;
@end

// some other header
@interface someClass : NSObject<CommandHandler>
{
// you will receive compiler warnings if you do not implement the protocol functions
}

Your handling and dispatch code is then written to work with objects of type "CommandHandler". E.g

-(void) registerForCommands:(CommandHandler*)handler
Andrew Grant
I'd prefer to use strings instead of pointers, as it would make debugging much easier. Look at NSStringFromSelector() and NSSelectorFromString().
jmah
+2  A: 

In Objective-C, you don't pass around methods; you pass around selectors, which are basically the canonical names of methods. Then, to make an object respond to a selector message, you send it performSelector:. For example:

NSString *exampleString = [NSString stringWithString:@"Hello"];
SEL methodName = @selector(stringByAppendingString:);
  // ^This is the selector. Note that it just represents the name of a
  //  message, and doesn't specify any class or implementation
NSString *combinedString = [exampleString performSelector:methodName withObject:@" world!"];

What you'll want is to make an array of NSStrings containing the names of the selectors you're interested in. You can use the function NSStringFromSelector() to do this. Then, when you want to use them, call NSSelectorFromString() on the strings to get the original selector back and pass it to the appropriate object's performSelector:. (As shown in the example above, the receiver isn't encoded in a selector — just the method name — so you might need to store the receiver as well.)

Chuck
A: 

Use NSValue.

For example:

NSArray* handlers = [NSArray arrayWithObjects:[NSValue valueWithPointer:handleA] ... ];

then to access :

handleptr* handle = (handlerptr*)[[handlers objectAtIndex:0] pointerValue];
handle(foo_bar);
terry franguiadakis