0

I have a bit of code that calls a method on an object whose .h file I don't have contained in my project, and cannot have contained (I don't want to get into this).

However, I know that I need to call a specific function that it does have, which returns an NSTimeInterval.

Obviously, the compiler warns me that the method is not defined and might crash. The problem is that the compiler defaults the return value of unknown functions to id, and i cannot cast an id to an non-pointer value. At runtime, however, the value of id is the value I need the NSTimeInterval to contain.

id tempValue = [myObject unknownNumberMethod]; // compiler says: "instance method -unknownNumberMethod not found(return type defaults to 'id'
NSTimeInterval realValue = (NSTimeInterval)tempValue; //this yields a compilation error:/Users/Niv/Projects/Copia/Copia.MAC/RMSDKServicer/RMSDKServicer/Downloader/ActivatorService.m:75:24: Initializing 'NSTimeInterval' (aka 'double') with an expression of incompatible type 'id'

I tried declaring the method like this, just to make the compiler understand it returns an NSTimeInterval and not an id:

@interface MyClass //type of myObject
-(NSTimeInterval)unknownNumberMethod;
@end

however, this makes the unknownNumberMethod return 0 constantly, so I assume it overwrites the real method with a blank one.

I also looked for some way to define an extern method of a class, but couldn't find the right syntax.

what is the correct way to "force" the compiler into realising that even though it doesn't find the method definition, it returns an NSTimeInterval and not an id?

dabhaid
  • 3,849
  • 22
  • 30
Niv
  • 2,294
  • 5
  • 29
  • 41
  • Don't you think it would be a better idea to find out *why* the compiler is complaining about `-unknownNumberMethod`? Do you really think you could get any useful value out of it? – Extra Savoir-Faire Jul 10 '14 at 14:50
  • I honestly do, on runtime i can see the value of id is correct. – Niv Jul 10 '14 at 15:02
  • I'm sure somebody smarter can improve on this, but you can get an NSString with the hex value from [NSString stringWithFormat@"%p", returnValue] – danh Jul 10 '14 at 15:09

4 Answers4

0

Have you tried this?

SEL selector = NSSelectorFromString(@"unknownNumberMethod");
if ([someInstance respondsToSelector:selector]) {
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
                            [[myObject class] instanceMethodSignatureForSelector:selector]];
    [invocation setSelector:selector];
    [invocation setTarget:myObject];
    [invocation invoke];
    NSTimeInterval realValue;
    [invocation getReturnValue:&realValue];
}
Bogdan Alexandru
  • 5,394
  • 6
  • 34
  • 54
  • results in 0 in realValue, while calling to the function from gdb shows the real value – Niv Jul 10 '14 at 15:05
0

Use an NSInvocation object instead of a performSelector call. See NSInvocation documentation

EricS
  • 9,650
  • 2
  • 38
  • 34
0

Restating my comment, maybe it's a bad idea, but one thing that probably can work is to render the id as a hex NSString using string formatting operation %p, then interpret that as a double...

id tempValue = [myObject unknownNumberMethod]; // compiler says: "instance method -unknownNumberMethod not found(return type defaults to 'id'
NSString *yuck = [NSString stringWithFormat:@"%p", tempValue];

NSTimeInterval result;
NSScanner *scanner = [NSScanner scannerWithString:yuck];
[scanner scanHexDouble:&result];
danh
  • 62,181
  • 10
  • 95
  • 136
0

If you don't have access to the header file, the correct thing to do it provide your own method declaration in an informal protocol:

@interface NSObject(MyAppExtension)
- (NSTimeInterval)unknownNumberMethod;
@end

You then invoke the method as you would any other:

id foo = ... 

NSTimeInterval timeInterval = 0.0;
if ([foo respondsToSelector:@selector(unknownNumberMethod)]) {
    timeInterval = [foo unknownNumberMethod];
    NSLog(@"Got Time Interval: %f", timeInterval);
}

this makes the unknownNumberMethod return 0 constantly, so I assume it overwrites the real method with a blank one.

That is incorrect; nothing is overwritten. Either an object provides an implementation for a given selector, or it doesn't.

If it's not returning the value you expect then the method has been declared with the incorrect return type. Try changing the return type on your method declaration to float or void*. A method that returns void* will have the same calling convention as a method that returns id on both 32-bit and 64-bit systems.

(If you're compiling for 32-bit iOS, that would explain why id works and NSTimeInterval does not. Declare the return type as float instead.)

Darren
  • 25,520
  • 5
  • 61
  • 71
  • I am compiling for 32-bit osx, inside an xpc service. This solution seems to work with NSString, however, still getting a 0 in the timeInterval. if I break at the assignment line and print out the rvalue of the assignment, I get the correct value. – Niv Jul 10 '14 at 17:47
  • Floating point values are normally returned via a x87 floating point register. It looks like that's not happening with your method (i.e., it's returning via a regular 32-bit register). To get the return value, you'll need to declare the return type as a non-floating point 32-bit value (like a pointer) – Darren Jul 10 '14 at 18:14