Creating a selector from a method name with parameters - objective-c

I have a code sample that gets a SEL from the current object,
SEL callback = #selector(mymethod:parameter2);
And I have a method like
-(void)mymethod:(id)v1 parameter2;(NSString*)v2 {
}
Now I need to move mymethod to another object, say myDelegate.
I have tried:
SEL callback = #selector(myDelegate, mymethod:parameter2);
but it won't compile.

SEL is a type that represents a selector in Objective-C. The #selector() keyword returns a SEL that you describe. It's not a function pointer and you can't pass it any objects or references of any kind. For each variable in the selector (method), you have to represent that in the call to #selector. For example:
-(void)methodWithNoParameters;
SEL noParameterSelector = #selector(methodWithNoParameters);
-(void)methodWithOneParameter:(id)parameter;
SEL oneParameterSelector = #selector(methodWithOneParameter:); // notice the colon here
-(void)methodWIthTwoParameters:(id)parameterOne and:(id)parameterTwo;
SEL twoParameterSelector = #selector(methodWithTwoParameters:and:); // notice the parameter names are omitted
Selectors are generally passed to delegate methods and to callbacks to specify which method should be called on a specific object during a callback. For instance, when you create a timer, the callback method is specifically defined as:
-(void)someMethod:(NSTimer*)timer;
So when you schedule the timer you would use #selector to specify which method on your object will actually be responsible for the callback:
#implementation MyObject
-(void)myTimerCallback:(NSTimer*)timer
{
// do some computations
if( timerShouldEnd ) {
[timer invalidate];
}
}
#end
// ...
int main(int argc, const char **argv)
{
// do setup stuff
MyObject* obj = [[MyObject alloc] init];
SEL mySelector = #selector(myTimerCallback:);
[NSTimer scheduledTimerWithTimeInterval:30.0 target:obj selector:mySelector userInfo:nil repeats:YES];
// do some tear-down
return 0;
}
In this case you are specifying that the object obj be messaged with myTimerCallback every 30 seconds.

You can't pass a parameter in a #selector().
It looks like you're trying to implement a callback. The best way to do that would be something like this:
[object setCallbackObject:self withSelector:#selector(myMethod:)];
Then in your object's setCallbackObject:withSelector: method: you can call your callback method.
-(void)setCallbackObject:(id)anObject withSelector:(SEL)selector {
[anObject performSelector:selector];
}

Beyond what's been said already about selectors, you may want to look at the NSInvocation class.
An NSInvocation is an Objective-C message rendered static, that is, it is an action turned into an object. NSInvocation objects are used to store and forward messages between objects and between applications, primarily by NSTimer objects and the distributed objects system.
An NSInvocation object contains all the elements of an Objective-C message: a target, a selector, arguments, and the return value. Each of these elements can be set directly, and the return value is set automatically when the NSInvocation object is dispatched.
Keep in mind that while it's useful in certain situations, you don't use NSInvocation in a normal day of coding. If you're just trying to get two objects to talk to each other, consider defining an informal or formal delegate protocol, or passing a selector and target object as has already been mentioned.

Related

How to invoke an Objective-C block obtained at runtime?

I'm trying to write a mock of HKHealthStore. In the stubbed executeQuery: I need to call the result handler block of a HKSampleQuery instance passed to it. The block is private so I need to get it at runtime. This is what I have so far:
- (void)executeQuery:(HKQuery *)query {
NSAssert([query isKindOfClass:HKSampleQuery.class], #"Mock executeQuery: not implemented yet for other query types than HKSampleQuery");
HKSampleQuery *sampleQuery = (HKSampleQuery *)query;
NSMutableArray<HKObject *> *queryResults = [NSMutableArray new];
for (HKObject *o in self.storedObjects) {
if ([sampleQuery.predicate evaluateWithObject:o]) {
[queryResults addObject:o];
}
}
SEL selector = NSSelectorFromString(#"resultHandler");
Method m = class_getInstanceMethod(HKSampleQuery.class, selector);
IMP imp = method_getImplementation(m);
typedef void(*resultHandler_t)(id, SEL, void(^)(HKQuery*, NSArray*, NSError*));
resultHandler_t f = (resultHandler_t)imp;
// here, I need to invoke the result handler block with sampleQuery, queryResults and nil as arguments
}
Note the selector name is "resultHandler" even though the parameter of the initializer of HKSampleQuery is called "resultsHandler".
Is there any way to invoke the block with appropriate arguments?
You're not doing what you think you are doing. You are getting the implementation of the method resultHandler (the getter method of the property resultHandler). The block you want is the value of the property resultHandler, which is the return value of running the getter method. In other words, you need to run the getter and get the result, not get the getter itself.
Simplest way to call the method and get the return value (since in this case the return value is a regular object pointer type) would be
typedef void (^resultHandler_t)(HKSampleQuery *query, NSArray *results, NSError *error);
resultHandler_t f = [sampleQuery performSelector:#selector(resultHandler)];
f(sampleQuery, queryResults, nil);
Alternately, if you declare (but not implement) the instance method or property resultHandler in a dummy category of HKSampleQuery, you can then access the property directly like resultHandler_t f = sampleQuery.resultHandler;

Programmatically access method arguments in Objective-C

Let's say I have the following method:
-(void)methodWithVar1:(NSString*)var1 var2:(NSString*)var2;
Now of course I can access var1 and var2 within methodWithVar1:var2: by using the variable names directly. But what I'd like to do is something like methodArgs[0] and methodArgs[1] to access the variables.
Is there a way to do this? I've looked at runtime.h but can't see anything that helps.
Why do I want to do this?
In certain circumstances, when a method is called, I want to prevent the method executing, but allow it to execute at another moment in time. I'm doing this by creating an NSInvocation object that allows me to 're-call' the method when I would prefer it. However, NSInvocation requires that I call setArgument:atIndex:, which I have to do manually. If the method every changes, then the population of NSInvocation needs to be updated. I want to avoid updating it manually and have a generic way of doing it.
Example
-(void)methodWithVar1:(NSString*)var1 var2:(NSString*)var2{
if (someCheckToSeeIfICannotRun) {
NSMethodSignature * methodSignature =
[self.class instanceMethodSignatureForSelector:_cmd];
KWInvocation * invocation =
[KWInvocation invocationWithMethodSignature:methodSignature];
[invocation.invocation setTarget:self];
[invocation.invocation setSelector:_cmd];
[invocation.invocation setArgument:&var1 atIndex:2];// This and
[invocation.invocation setArgument:&var2 atIndex:3];// this, I would prefer to make generic
[invocation.invocation retainArguments];
//
// Store invocation somewhere so I can call it later...
//
}
else {
// Let the method run as normal
}
}
I think this can help you:
#import <objc/runtime.h>
void logArguments(const id* selfPtr)
{
id obj = *selfPtr;
SEL sel = *(SEL*)(void*)(--selfPtr);
Method m = class_getInstanceMethod([obj class], sel);
for (unsigned int cnt = method_getNumberOfArguments(m) - 2; 0 != cnt; --cnt)
{
NSLog(#"arg: %#", *(--selfPtr));
}
}
You can call logArguments(&self); in any method, but there is one restriction: all arguments should be objective-c objects.

Objective C run-time parameter binding

I'd like (at runtime) to bind a parameter to a function as you can do in boost::bind - a little like the following:
-(void)myFuncWithParameter:(NSString*)param {
NSLog(param);
}
-(void)init {
UIButton *helloButton = [UIButton buttonWithType:UIButtonTypeCustom];
[helloButton addTarget:self action:#selector(myFuncWithParameter:#"hello") forControlEvents:UIControlEventTouchUpInside];
}
So... I'm dynamically binding (at runtime) the value #"hello" to a parameter.
Obviously the above isn't the correct Syntax. Does anyone know if this is possible and the correct syntax?
Cheers,
Nick.
The short answer is no, or at least not at that level.
The long answer is that it is technically possible to build something akin to using NSInvocations (and/or forwardInvocation:), doing something clever in methodForSelector: and or by dynamically registering method implementations, but it is very tricky, especially if you care at all about speed.
If I had some code where building curried methods like that was really worthwhile, what I would do is something like this (written in this comment, untested);
//FIXME: In a real implementation you would do some mangling, this code will get confused if you have _s in the curried selector, and thus could be exploitable
//This method makes a unique selector by mangling the arguments
- (SEL) selectorForSelector:(SEL)bindSel withString:(NSString *)bindString {
NSString *mangle = [NSString *stringWithFormat:#"LGBind_%#_%#"], NSStringFromSelector(bindSel), bindString];
SEL retval = NSSelectorFromString(mangle);
//Register the imp. You probably want to check if it is already reg
if (![self respondsToSelector:retval]) {
class_addMethod([self class], retval, LGBind_IMP, "v#:")l
}
}
//Generic dispatcher imp
void LGBind_IMP(id self, SEL _cmd) {
NSString *selectorName = NSStringFromSelector(_cmd);
NSArray *array [selectorName componentsSeparatedByString:#"_"];
//Skip index 0; it is #"LGBind"
NSString *originalSelectorString = [array objectAtIndex:1];
NSString *originalArgString = [array objectAtIndex:2];
//Get our the SEL and the IMP
SEL originalSEL = NSSelectorFromString(originalSelectorString);
IMP originalIMP = [self methodForSelector:originalSEL];
//call the original imp
originalIMP([self class], originalSEL, originalArgString);
}
Obviously depending on your exact needs you could do things somewhere differently, for instance you could lazily by the imps in forwardInvocation, or stash data about the managled selector in a dict in the instance instead of just managling it into the selector name.
The general answer is that the target-action mechanism only allows for a target, a sender and a message that takes the sender; therefore, if you need to access data, you must get it from the target or the sender.
One option would be to create a class that represents the binding of a parameter value, a method and an object. This class would have an action that invokes the method on the object, passing the value. Use an instance of this class as the target. Here's a simplistic example:
#interface UnaryBinder : NSObject {
id target;
SEL selector;
id parameter;
}
#property id target;
#property SEL selector;
#property (retain) id parameter;
-(id)initWithTarget:(id)anObject selector:(SEL)aSelector param:(id)aParameter;
-(void)action:(id)sender;
#end
#implementation UnaryBinder
...
-(void)action:(id)sender {
[target performSelector:selector withObject:parameter];
}
#end
If you want to support an arbitrary number of parameters, you'd need to use NSInvocation (as Louis mentions) rather than performSelector:withObject. Of course, controls don't retain their targets, so you need some way of keeping the UnaryBinder around. At that point, you might as well skip the special class and just store the data in the control, as you mention in your comment about using KVP. Alternatively, factor out the action into a controller class and use an instance of that as the target. UnaryBinder and its ilk doesn't really offer any advantages when it comes to target-action. For related topics, google "higher order messaging".

How to build a NSArray (or NSMutableArray) of class methods in Objective-C?

I'm trying to build a NSArray of methods in Objective-C.
(What I'm trying to accomplish here is something like the following in C)
typedef (void)(*handler)(int command);
void handleCommandA(void) { ... }
void handleCommandB(void) { ... }
static const handler handler_table[10] = {
handleCommandA, handleCommandB, handleCommandC
};
I have to port this to Objective-C and I don't know how to
build an array of function pointers (in Objective-c world,
class methods) at compile-time.
In Objective-C I have the following.
- (void)handleCommandA { ... }
- (void)handleCommandB { ... }
/* Now how to add above 2 functions into NSArray? */
NSArray *handler_table = [NSArray arrayWithObjects:... ]; /* This doesn't seem to work. */
The problem here is that to bind those functions you must use the selector keyword which returns a SEL type. This is a pointer type whereas NSArray stores objects.
You thus have three options;
Use a regular C-type array
Fold the functions into an NSObject derived class that will call them.
Use a protocol.
The second is likely the nicer and for this you can use the NSValue class to hold the selector results. E.g;
NSValue* selCommandA = [NSValue valueWithPointer:#selector(handleCommandA:)];
NSValue* selCommandB = [NSValue valueWithPointer:#selector(handleCommandB:)];
NSArray *handler_table = [NSArray arrayWithObjects:selCommandA, selCommandB, nil ];
When you have retrieved the correct entry from the array, to convert back you would do;
SEL mySelector = [selCommand pointerValue];
[someObject performSelector:mySelector];
(Note I'm assuming that from your objective-c syntax that these are intended to be used as methods on an object and not global functions. If you wish to use them globally then you should write them as you would in plain C.)
Another option is to formalize the command methods into a protocol. This allows you to write functionality that will work on any object which implements that protocol and the compiler will provide more checking than if you were just calling selectors.
E.g.
// some header
#protocol CommandHandler
#required
-(void) handleCommandA;
-(void) handleCommandB;
#end
// some other header
#interface someClass : NSObject<CommandHandler>
{
// you will receive compiler warnings if you do not implement the protocol functions
}
Your handling and dispatch code is then written to work with objects of type "CommandHandler". E.g
-(void) registerForCommands:(CommandHandler*)handler
Use NSValue.
For example:
NSArray* handlers = [NSArray arrayWithObjects:[NSValue valueWithPointer:handleA] ... ];
then to access :
handleptr* handle = (handlerptr*)[[handlers objectAtIndex:0] pointerValue];
handle(foo_bar);
In Objective-C, you don't pass around methods; you pass around selectors, which are basically the canonical names of methods. Then, to make an object respond to a selector message, you send it performSelector:. For example:
NSString *exampleString = [NSString stringWithString:#"Hello"];
SEL methodName = #selector(stringByAppendingString:);
// ^This is the selector. Note that it just represents the name of a
// message, and doesn't specify any class or implementation
NSString *combinedString = [exampleString performSelector:methodName withObject:#" world!"];
What you'll want is to make an array of NSStrings containing the names of the selectors you're interested in. You can use the function NSStringFromSelector() to do this. Then, when you want to use them, call NSSelectorFromString() on the strings to get the original selector back and pass it to the appropriate object's performSelector:. (As shown in the example above, the receiver isn't encoded in a selector — just the method name — so you might need to store the receiver as well.)

Handling Callbacks

I have a method in an objective-C class. It has 2 callback functions written in C. The class pointer i.e. self is passed to these functions as void *. In the C functions I create a pointer of type class and assign the void * parameter.
The first callback function executes successfully. But the void * pointer becomes nil in the 2nd callback function. Note that I haven't tweaked pointer in the first callback but still I get nil in 2nd callback.
Any ideas what might be going wrong?
For example:
kr = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification,
matchingDict, RawDeviceAdded, NULL,
&gRawAddedIter);
RawDeviceAdded(NULL, gRawAddedIter, self);
This works fine. But below function receives self as nil.
kr = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification,
matchingDict, BulkTestDeviceAdded, NULL,
&gBulkTestAddedIter);
BulkTestDeviceAdded(NULL, gBulkTestAddedIter, self);
Are your problems specifically with the IOKit callback routines? The problem with the specific example you gave is that the IOServiceMatchingCallback takes only 2 parameters, not 3. You need your RawDeviceAdded() and BulkTestDeviceAdded() callback functions to match the IOServiceMatchingCallback prototype and to accept self as the first parameter (refCon), not the 3rd. Also, you need to pass in self as the second-to-last parameter of IOServiceAddMatchingNotification() to get it passed back to you by the callback.
A common method for handling C callbacks in Objective-C code is just to have a static function that forwards the callback to your instance. So, your example callback code would look like this:
static RawDeviceAdded(void* refcon, io_iterator_t iterator)
{
[(MyClass*)refcon rawDeviceAdded:iterator];
}
#implementation MyClass
- (void)setupCallbacks
{
// ... all preceding setup snipped
kr = IOServiceAddMatchingNotification(gNotifyPort,kIOFirstMatchNotification, matchingDict,RawDeviceAdded,(void*)self,&gRawAddedIter );
// call the callback method once to 'arm' the iterator
[self rawDeviceAdded:gRawAddedIterator];
}
- (void)rawDeviceAdded:(io_iterator_t)iterator
{
// take care of the iterator here, making sure to complete iteration to re-arm it
}
#end
Generally, callbacks in Objective-C are handled by passing a delegate object and a selector to perform on that delegate. For example, this method will call a method on its delegate after logging a message, passing both itself and the message that was logged.
- (void)logMessage:(NSString *)message
delegate:(id)delegate
didLogSelector:(SEL)didLogSelector
{
NSLog(#"%#", message);
if (delegate && didLogSelector && [delegate respondsToSelector:didLogSelector]) {
(void) [delegate performSelector:didLogSelector
withObject:self
withObject:message];
}
}
You might call it in code like this:
- (void)sayHello
{
[logger logMessage:#"Hello, world"
delegate:self
didLogSelector:#selector(messageLogger:didLogMessage:)];
}
- (void)messageLogger:(id)logger
didLogMessage:(NSString *)message
{
NSLog(#"Message logger %# logged message '%#'", logger, message);
}
You can also use objc_msgSend() directly instead, though you need to understand the Objective-C runtime enough to choose which variant to use and how to construct the prototype and function pointer through which to call it. (It's the mechanism by which message sends are actually implemented in Objective-C — what the compiler normally generates calls to in order to represent [] expressions.)
This is what Objective-C's selector is for:
http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/NSInvocationOperation_Class
The API isn't very intuitive, but its fine once you understand it
You might need to do some refactoring as well, now there might be a better way, but when I had this problem my solution was to refactor and use InvoationOperation.