Quick question. Does anyone know how to get the function pointer of an objective c method? I can declare a C++ method as a function pointer, but this is a callback method so that C++ method would need to be part of the class SO THAT IT CAN ACCESS THE INSTANCE FIELDS. I don't know how to make a C++ method part of an objective c class. Any suggestions?
问题:
回答1:
Typically, you need two pieces of information to call back into Objective-C; the method to be invoked and the object to invoke it upon. Neither just a selector or just the IMP -- the instanceMethodForSelector:
result -- will be enough information.
Most callback APIs provide a context pointer that is treated as an opaque value that is passed through to the callback. This is the key to your conundrum.
I.e. if you have a callback function that is declared as:
typedef void (*CallBackFuncType)(int something, char *else, void *context);
And some API that consumes a pointer of said callback function type:
void APIThatWillCallBack(int f1, int f2, CallBackFuncType callback, void *context);
Then you would implement your callback something like this:
void MyCallbackDude(int a, char *b, void *context) {
[((MyCallbackObjectClass*)context) myMethodThatTakesSomething: a else: b];
}
And then you would call the API something akin to this:
MyCallbackObjectClass *callbackContext = [MyCallbackObjectClass new];
APIThatWillCallBack(17, 42, MyCallbackDude, (void*)callbackContext);
If you need to switch between different selectors, I would recommend creating a little glue class that sits between the callback and the Objective-C API. The instance of the glue class could contain the configuration necessary or logic necessary to switch between selectors based on the incoming callback data.
回答2:
You can do it with @selector
and the SEL
type:
SEL sel = @selector(myMethod:);
/* Equivalent to [someObject myMethod:Paramater] */
[someObject performSelector:sel withObject:Parameter]
There is also the IMP
type...
IMP imp = [someObject methodForSelector:sel];
/* don't remember if this syntax is correct; it's been a while...
* the idea is that it's like a function pointer. */
imp(someObject, sel, Parameter);
Update based on your comments
If you don't want to have to specify the object, in this case you are asking for something awfully non-portable. Essentially you want lambda expressions which is not a feature of C, though it's coming in C++0x.
So my solution is going to be "out there", sketchy, and non portable...
BUT... maybe you can do it with runtime code generation...
You can start by writing a stub function in assembly (assuming you want x86)
push dword 0 ; SEL will go here
push dword 0 ; Object will go here
push dword 0 ; IMP will go here
pop eax ; eax = imp
call eax ; call imp
add esp, 8 ; cleanup stack
ret ; return
This assembles to:
0068 0000 6800 0000 0000 0068 0000 5800 d0ff c481 0004 0000 00c3
Note the instruction push dword 0
is the bytes 68 00 00 00 00
. We will fill in the zeros to a pointer at runtime.
So, we can copy that into a malloc()
'd buffer, patch it up, and call mprotect()
to make it executable.
Below code is for illustrative purposes and I have no idea if it works. :-)
/* x86 code... */
char code[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0x68,
0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x00, 0x00, 0x58, 0xff, 0xd0,
0x81, 0xc4, 0x04, 0x00, 0x00, 0x00,
0xc3 };
char *buf = malloc(sizeof(code));
SEL Selector = @selector(Method);
IMP Imp = [object methodForSelector:Selector];
/* Copy template */
memcpy(buf, code, sizeof(code));
/* Patch the "push dword 0" parts with your arguments
* This assumes everything is 32-bit, including SEL, IMP, etc. */
memcpy(buf + 1, &Selector, sizeof(Selector));
memcpy(buf + 6, &object, sizeof(object));
memcpy(buf + 11, &Imp, sizeof(Imp));
/* Now here comes the sketchy part...
* Make it executable and turn it into a function pointer. */
mprotect(buf, sizeof(code), PROT_EXEC);
void (*Function)() = (void(*)())buf;
/* Now, crazy as it sounds, you should be able to do: */
Function();
You might want to do an [object retain]
for as long as this function exists, and [object release]
when and if you should choose to free it. (Probably best to wrap this sketcyness inside an object anyway, then use normal objc refcounting to control the buffer and a reference to object
.) Maybe you'll also want to use mmap()
to allocate instead of malloc()
...
If that sounds needlessly complex that's because it is. :-)
回答3:
You can create a C++ class as a wrapper. And call the objective C message from that class. Just remember to make your file yoursource.mm instead of yoursource.cpp.
回答4:
You can do this with instanceMethodForSelector:
See the Apple NSObject documentation for more info.