Running performSelector: on an object that returns a double, not id
Asked Answered
J

3

5

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.

Joplin answered 11/11, 2011 at 0:10 Comment(4)
Why NSInteger? Why not NSNumber?Foraminifer
Xcode compiler prevents casting to anything other than NSInteger. Also, NSNumber is an object, but the method returns a double, not an object.Joplin
But you can init NSNumber with any primitive. Including double.Foraminifer
Ok, my mind just didn't want to think right, I see your problem now.Foraminifer
J
4

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));
Joplin answered 11/11, 2011 at 0:35 Comment(6)
Maybe you would explain why you think this is a better solution?Reprovable
No problem : ) In that specific case, I think using objc_msgSend_fpret is better, and actually more readable. You should always use function's casts with caution.Reprovable
Definitely more readable. Safety-wise, not sure what happens internally, but I can't imagine it does much more than expand to the IMP solution.Joplin
Actually, all the objc_msgSend* functions are implemented using assembly, so it might do a lot of things differently.Reprovable
@Reprovable I tried your method in favor of readability, but it doesn't work. It only returns NAN, whereas the IMP method returns the valid results. Take for example ` NSTimeInterval t = objc_msgSend_fpret([NSDate date], @selector(timeIntervalSinceReferenceDate));` Returns NAN.Joplin
Ok, see it now... Check the edit on my answer in a few minutesReprovable
R
8

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
Reprovable answered 11/11, 2011 at 0:30 Comment(4)
objc_msgSend and friends are declared in objc/message.h. On Mac OS X, you can just import objc/objc-runtime.h, but on iOS you must import objc/message.h. I edited the answer.Jonis
@Reprovable Weird thing, but although it should work, it returns NAN, where the IMP method returns the valid result. Not sure if I'm missing something.Joplin
@Reprovable Chuck would win, but I don't like that it creates an NSNumber object, just to be cast to doubleValue. Plus, msgSend still doesn't work... ` long double x = objc_msgSend_fpret( [NSDate date], @selector( timeIntervalSinceReferenceDate ) );` x = NAN.Joplin
There's no prefect way. It all depends on what you want. Often, when dealing with such problems, performance may lead to a different approach (which can also lead to problems). It's also different if you actually wrote your blah method, or if it comes from a library/framework. Hope you'll find the right way : )Reprovable
G
7

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.

Goncourt answered 11/11, 2011 at 0:30 Comment(3)
The value that's returned from that method is a double, so using 'doubleValue' on it or 'valueForKey:' doesn't make sense.Joplin
@David: valueForKey: boxes primitives, so yes, what I said does make sense.Goncourt
I stand corrected. I like your solution. It'd be the best, except it creates a temp NSNumber object.Joplin
J
4

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));
Joplin answered 11/11, 2011 at 0:35 Comment(6)
Maybe you would explain why you think this is a better solution?Reprovable
No problem : ) In that specific case, I think using objc_msgSend_fpret is better, and actually more readable. You should always use function's casts with caution.Reprovable
Definitely more readable. Safety-wise, not sure what happens internally, but I can't imagine it does much more than expand to the IMP solution.Joplin
Actually, all the objc_msgSend* functions are implemented using assembly, so it might do a lot of things differently.Reprovable
@Reprovable I tried your method in favor of readability, but it doesn't work. It only returns NAN, whereas the IMP method returns the valid results. Take for example ` NSTimeInterval t = objc_msgSend_fpret([NSDate date], @selector(timeIntervalSinceReferenceDate));` Returns NAN.Joplin
Ok, see it now... Check the edit on my answer in a few minutesReprovable

© 2022 - 2024 — McMap. All rights reserved.