Return value for performSelector:
Asked Answered
R

5

41

What will the return value for performSelector: if I pass a selector that returns a primitive type (on object), such as 'week' on NSDateComponents (which will return an int)?

Restaurant answered 27/6, 2011 at 11:18 Comment(0)
L
85

An example of using NSInvocation to return a float:

SEL selector = NSSelectorFromString(@"someSelector");
if ([someInstance respondsToSelector:selector]) {
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
                                [[someInstance class] instanceMethodSignatureForSelector:selector]];
    [invocation setSelector:selector];
    [invocation setTarget:someInstance];
    [invocation invoke];
    float returnValue;
    [invocation getReturnValue:&returnValue];
    NSLog(@"Returned %f", returnValue);
}
Lepidosiren answered 11/6, 2012 at 2:7 Comment(3)
Apple's guide on using NSInvocationEverara
Although this was a very useful code snippet, I would recommend against the test of [someInstance respondsToSelector:x]. I would much rather have my code crash than silently bypass an expected invocation.Hereditable
@Hereditable as with every part of code, there are many possible use cases. Some people need it for something far less important, so it's safer to silently bypass than crash. It's good that dizy presented safe-check, as people may select to use it or not. Otherwise some devs could not think to check (or they don't know how and just quickly code without research) while they should in their case.Hultin
M
9

I think you cannot get the return value from performSelector. You should look into NSInvocation.

Mispronounce answered 27/6, 2011 at 11:23 Comment(2)
I might have to use NSInvocation, but you can get the return value of performSelector:, at least if it's an object.Restaurant
The docs specifically state that you must use NSInvocation if the method you're calling via ‑performSelector: returns something other than an object.Chellman
A
6

New answer to old question~

There is a more concise way of getting the return value from performSelector

NSInvocationOperation *invo = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(height) object:nil];
[invo start];
CGFloat f = 0;
[invo.result getValue:&f];
NSLog(@"operation: %@", @(f));

in which

- (CGFloat)height {
    return 42;
}

output

2017-03-28 16:22:22.378 redpacket[46656:7361082] operation: 42

Arnuad answered 28/3, 2017 at 9:3 Comment(0)
S
3

To answer the second part of the question, another way to invoke a selector that returns a primitive would be to get a function pointer and invoke it as such, as in (assuming someSelector returns a float and has no arguments);

SEL selector = NSSelectorFromString(@"someSelector");
float (*func)(id,SEL) = (float (*)(id,SEL))[someInstance methodForSelector: selector];
printf("return value is: %f", (func)(someInstance, selector));
Soapbox answered 23/1, 2015 at 3:40 Comment(1)
Float return values might not be ABI compatible on x86 (i.e 32 bit simulator). Actual devices seem fine though (arm, arm64). developer.apple.com/library/mac/documentation/Cocoa/Reference/…Boycott
S
2

I tried the NSInvocation implemented as suggested by dizy, its working as expected.

I also tried the another way i.e.

int result = objc_msgSend([someArray objectAtIndex:0], @selector(currentPoint));

in the above case, we are bypassing the compiler and inserting objc_msgSend call explicitly as described in the blog: http://www.cocoawithlove.com/2011/06/big-weakness-of-objective-c-weak-typing.html

In this case, I got the following warning: Implicitly declaring library function 'objc_msgSend' with type 'id (id, SEL, ...)' which is obvious because we are calling library function directly.

So, I implemented with the NSInvocation which is working perfectly fine for me.

Signore answered 23/4, 2015 at 10:37 Comment(2)
Did you #include <objc/message.h>?Ransell
No, I didn't includeSignore

© 2022 - 2024 — McMap. All rights reserved.