Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
806 views
in Technique[技术] by (71.8m points)

cocoa - using objc_msgSend to call a Objective C function with named arguments

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];

??

Best Markus

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...