问题
How can I run an arbitrary selector on an object, the return of which is a double? For example, I have obj A, which has method -(double)blah;
How can I do double res = [obj performSelector:@selector(blah)];
? performSelector returns an id
type object, so should I cast from id to NSInteger to double - that will lose precision?
Also, I do not want to use the obj's methodSignatureForSelector
(meaning, no NSMethodSignature
and no NSInvocation
) because it is a huge CPU drain at run-time.
回答1:
You may want to take a look at the Objective-C runtime functions, especially objc_msgSend_fpret
.
double objc_msgSend_fpret( id self, SEL op, ... )
Which sends a message with a floating-point return value to an instance of a class.
The performSelector
methods use objc_msgSend
, which returns an id
type.
For instance:
double res = objc_msgSend_fpret( obj, @selector( blah ) );
You'll need to import this objc runtime header:
#import <objc/message.h>
EDIT
By the way, here's the link to the ObjC runtime reference: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html
EDIT 2 - IMPORTANT
objc_msgSend_fpret
is implemented in different ways, depending on the CPU architecture (basically, i386 or x86_64).
As I said in a comment, those functions are implemented using assembly, so their implementations depends on the CPU architecture.
Under the x86_64 architecture, this function returns a long double
.
This is why it fails (and returning NAN) when you assign it to a double
.
Also note that there is an objc_msgSend_fp2ret function.
So, basically, my previous example will not work:
double x = objc_msgSend_fpret( obj, @selector( blah ) );
printf( "Val: %f\n", x );
As you noticed, it will print 'NAN'.
To make it work, you'll have to do it this way:
long double x = objc_msgSend_fpret( obj, @selector( blah ) );
printf( "Val: %Lf\n", x );
Here's a working example:
http://www.eosgarden.com/uploads/misc/fp.m
Compile it using:
gcc -Wall -framework Foundation -o fp fp.m
回答2:
If it's a method with no arguments, you can use valueForKey:
and doubleValue
on the value returned from that method. Otherwise, I think you'll have to muck with objc_msgSend_fpret
to make it work.
回答3:
Uses least memory (valueForKey: uses two temporary objects) and the msgSend method doesn't work.
IMP myImp1 = [obj methodForSelector:@selector(getDouble)];
double aDouble1 = ((double (*) (id,SEL))myImp1)(obj,@selector(getDouble));
来源:https://stackoverflow.com/questions/8088145/running-performselector-on-an-object-that-returns-a-double-not-id