I want to add scripting support for an Objective-C project using the objc runtime.
Now I face the problem, that I don't have a clue, how I should call an Objective-C method
which takes several named arguments.
So for example the following objective-c call
[object foo:bar];
could be called from C with:
objc_msgSend(object, sel_getUid("foo:"), bar);
But how would I do something similar for the method call:
[object foo:var bar:var2 err:errVar]; ======================================================================= 回答1.
objc_msgSend(object, sel_getUid("foo:bar:err:"), var, var2, errVar);
If one of the variables is a
float
, you need to use @Ken's method,or cheat by a reinterpret-cast:
objc_msgSend(..., *(int*)&var, ...)
Also, if the selector returns a float, you may need to use
objc_msgSend_fpret
,and if it returns a struct you must use
objc_msgSend_stret
.If that is a call to superclass you need to use
objc_msgSendSuper2
.回答2
转载至:The accepted answer is close, but it won't work properly for certain types.
For example, if the method is declared to take a
float as its second argument, this won't work.
To properly use objc_msgSend, you have to cast it to the the appropriate type.
For example, if your method is declared as
- (void)foo:(id)foo bar:(float)bar err:(NSError **)err
then you would need to do something like this:
void (*objc_msgSendTyped)(id self, SEL _cmd, id foo, float bar, NSError**error) = (void*)objc_msgSend; objc_msgSendTyped(self, @selector(foo:bar:err:), foo, bar, error);
Try the above case with just objc_msgSend, and log out the received arguments.
You won't see the correct values in the called
function. This unusual casting situation arises because objc_msgSend is not
intended to be called like a normal C function.
It is (and must be) implemented in assembly, and just
jumps to a target C function
after fiddling with a few registers.
In particular, there is no consistent way to refer to any
argument past the first
two from within objc_msgSend.
Another case where just calling objc_msgSend straight wouldn't
work is a method that
returns an NSRect, say, because
objc_msgSend is not used in that case, objc_msgSend_stret is.
In the underlying C function
for a method that returns an
NSRect, the first argument is actually a pointer to an out value NSRect,
and the function
itself actually returns void.
You must match this convention when calling because it's what the called
method will assume.
Further, the circumstances in which objc_msgSend_stret is used differ
between architectures.
There is also an objc_msgSend_fpret, which should be used for methods
that return certain
floating point types on certain
architectures.
Now, since you're trying to do a scripting bridge thing, you probably
cannot explicitly cast
every case you run across,
you want a general solution. All in all, this is not completely trivial,
and unfortunately your
code has to be specialized to
each architecture you wish to target (e.g. i386, x86_64, ppc).
Your best bet is probably to
see how PyObjC does it.
You'll also want to take a look at libffi. It's probably a good idea
to understand a little
bit more about how parameters
are passed in C, which you can read about in the Mac OS X ABI Guide. Last,
Greg Parker, who
works on the objc runtime,
has written a bunch of very nice posts on objc internals.
http://stackoverflow.com/questions/2573805/using-objc-msgsend-to-call-a-objective-
c-function-with-named-arguments/2573949#2573949