Use performSelector with three or more arguments? - objective-c

The various performSelector:... methods can handle a maximum of two arguments passed to the specified selector. What can I do if I need to pass three or more arguments?

You need to use NSInvocation class for that. Check this SO question for more details on using them.

I dislike the NSInvocation way, it needs too much code.
If you’d like perform the selector immediately, here is an simple and clean way:
// Assume we have these variables
id target, SEL aSelector, id parameter1, id parameter2;
// Get the method IMP, method is a function pointer here.
id (*method)(id, SEL, id, id) = (void *)[vc methodForSelector:aSelector];
// IMP is just a C function, so we can call it directly.
id returnValue = method(vc, aSelector, parameter1, parameter2);

Related

How to pass more than One Variable Args at ObjC method?

I need to receive more than one variable Args in my method. But I don't know how to do so.
For example:
(void)insertInTableOnAttributes:(id)fieldsNames, ... Values:(id)fieldsValues, ...;
sadly it appears a compiler error after the first (...) saying:
Expected ':' after method Prototype".
And in the implementation says:
Expected Method Body" in the same position (just after the first ...)
PD: I'm using Xcode 4.2.1.
You can't do that. How would the generated code possibly know where one argument list ends and the next begins? Try to think of the C equivalent
void insertInTableOnAtributes(id fieldNames, ..., id fieldValues, ...);
The compiler will reject that for the same reason.
You have two reasonable options. The first is to provide a method that takes NSArrays instead.
- (void)insertInTableOnAttributes:(NSArray *)fieldNames values:(NSArray *)fieldValues;
The second is to use one varargs with a name-value pair, similar to +[NSDictionary dictionaryWithObjectsAndKeys:]
- (void)insertInTableOnAttributes:(id)fieldName, ...;
This one would be used like
[obj insertInTableOnAttributes:#"firstName", #"firstValue", #"secondName", #"secondValue", nil];
The C analogy is actually quite accurate. An Obj-C method is basically syntactic sugar on top of a C method, so
- (void)foo:(int)x bar:(NSString *)y;
is backed by a C method that looks like
void foobar(id self, SEL _cmd, int x, NSString *y);
except it doesn't actually have a real name. This C function is called the IMP of the method, and you can retrieve it using obj-c runtime methods.
In your case of having arguments after a varargs, your
- (void)someMethodWithArgs:(id)anArg, ... andMore:(id)somethingElse;
would be backed by an IMP that looks like
void someMethodWithArgsAndMore(id anArg, ..., id somethingElse);
and since you cannot have any arguments after a varargs, this simply won't work.
- (void)insertInTableOnAttributes:(NSArray *)fieldsNames values:(NSArray *)fieldsValues;
// Use
[self insertInTableOnAttributes:[NSArray arrayWithObject:#"name", nil] values:[NSArray arrayWithObject:#"value", nil]];

Calling method on dynamic Class in Objective-C?

How do I formulate
[NSClassFromString(classname) myMethod:param1 more:param2];
such that the compiler does not give a warning saying that +myMethod may not be implemented ?
[NSClassFromString(classname) performSelector: #selector(myMethod:more:) withObject:param1 withObject:param2];
Quick & dirty: cast the return of NSClassFromString to id, if myMethod:more: is unique. The method binding doesn't happen until runtime, so the correct impl will be called.
Slightly cleaner: use NSObject's -(id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject, if param1 and param2 are ids. It works for class methods too when called on a class object.
Well, since you have multiple arguments, you can’t use -performSelector:withObject:. You’ll have to use what Objective-C uses under the hood, objc_msgSend(). But first you’ll have to cast it. Here’s how:
In your implementation file (.m), add the line #import <objc/message.h> to the top. Then, you need to cast objc_msgSend() appropriately. In this example, we’ll assume that param1 and param2 are Objective-C objects and that -myMethod:more: returns void.
void (*myMsgSend)(id self, SEL _cmd, id param1, id param2);
myMsgSend = (void(*)(id, SEL, id, id))objc_msgSend;
Once you’ve cast it appropriately, call your new function:
myMsgSend(obj, #selector(myMethod:more:), param1, param2);
Try typecasting the value returned by NSClassFromString() to id first.

passing function reference to a C pointer type, C will invoke the function

I have a simple question i.e. how can I pass objective-C function reference as a C function pointer so that C can invoke that function.
edit: Sorry for not providing the sample source here it is:
- (void)init {
CLibStructure cLibObject;
cLibObject.on_work_done = &cWorkDone;
}
the function that will point to on_work_done will have this signature in C
static void cWorkDone(const char *workInfo);
whereas in objective-C this is the signature that I made
- (void) workDoneWithStatusMessage:(const char *message);
Now i want is to point cLib.on_work_done the pointer to objective-c function, if I point to standard C function it works.
In short, you can't. Not directly.
A method call is a combination of a target, the object to message, and the selector that identifies the method to call.
You need to bundle those up together somehow. With Blocks it is easy enough. With pure C APIs, it can typically be done with a context pointer or something like it.
Given that you posted no code, no examples, none of the API to be used, and none of the details about the C API itself, providing details is difficult.
You can create a function that calls the method, and then manipulate a pointer to that:
int function(NSObject *something, int someArg)
{
return [something thisIsAMethod:someArg];
//assuming NSObject has a method called thisIsAMethod that takes an int as a parameter and returns an int.
}
and then you could use a pointer to this function:
int (*pointerToFunction)(NSObject *, int) = &function;

Elementary Obj-C Question bout Methods

take example:
-(void)setName:(NSString *)name age:(int)age;
How would you call this method (in other words, the method's name is setName but what is the "age" parameter doing in there) and what do the types in parentheses mean? Is it just a way to tell the compiler what types are being returned?
[ myObject setName: #"Adam" age:18 ];
The age parameter is the second parameter in the method signature.
The types in parentheses are the expected types for the argument. e.g. name is expecting only an NSString and age is expecting only an int.
The - means that the method is an instance method, not a class method, which is denoted using a + instead.
The type in parentheses right after the - is the return type.
This is a great site for learning the basics of Objective-C: CocoaDevCentral
To answer, one would need a bit more information, but I'll be guessing this is from some sort of class named aClass, and you have an instance of aClass, named instance.
-(void)setName:(NSString *)name age:(int)age;
means you have a method, named setName:age:, that needs two arguments, one NSString, one int, and it returns a void. As it has a - as it's first character, it is an instance method.
[instance setName:#"James Hargrove" age:21];
Would call setName:age: on the instance.
(The instance should be created using, say,
aClass *instance = [[aClass alloc] init];
which would create an instance of aClass named instance, and initialize it.
This is the standard Objective-C method syntax. This could be read as:
A method with no return value (void) that
sets the name of the object (an NSString * parameter)
and the age (and integer
parameter).
Dissecting the method:
"-" The hyphen states that this is an instance method.
(void) The return type is void - or
no return type expected
setName:(NSString *) The first
parameter to be passed is the "name"
and is an NSString *.
age:(int)age The second parameter
to be passed is the "age" and is
an int.
In reality, the method syntax is actually quite self-documenting once understood (and quite foreign if you're used to more tradition C/C++ or Java syntax).
The actual example of the call of this method would be:
[someObject setName:#"Rich" age:101];
The method name is actually this:
setName:age:
You call it like this:
[someObject setName:#"Alice" age:20];
setName:age: is also the unique signature of that method, and with that signature you can call that method on any object you wish. For example:
NSArray* objects = ...
SEL mySelector = #selector(setName:age:);
for (id object in objects)
{
if ([object respondsToSelector:mySelector])
{
[object setName:#"Alice" age:20];
}
}
what do the types in parentheses mean? Is it just a way to tell the compiler what types are being returned?
Yes, those are "C casts". If everything was an object you wouldn't need those, but because you can pass and return plain old C types to and from your methods, the compiler needs to know the types of your parameters and return values.
You'd call this method like so:
[classInstance setName:#"name" age:123];
The first instance of "age:" indicates that the method receives another parameter, called "age" when used in the implementation of the method.
The types in parentheses indicate the types of data that are expected for each parameter, with the exception of the first one, "void", which means that this method returns nothing.
So, you would call this method as follows.
Say it is a method of an object named foo (of class Foo). Then you would call:
[foo setName:someName age:someAge].
If it were a static method, it would be preceded by a + instead of a minus as follows:
+(void)setName:(NSString *)name age:(int)age;
Then you would call
[Foo setName:someName age:someAge] //use the classname instead of the object name
The types are indeed there for type-checking by the compiler. You'll get warnings if you pass the wrong sort of data, and you will get warnings if your header doesn't match your implementation.
You can actually write Obj-C functions in a couple of different styles though, omitting some of this stuff. You can even write straight up C-style.

How can I dynamically create a selector at runtime with Objective-C?

I know how to create a SEL at compile time using #selector(MyMethodName:) but what I want to do is create a selector dynamically from an NSString. Is this even possible?
What I can do:
SEL selector = #selector(doWork:);
[myobj respondsToSelector:selector];
What I want to do: (pseudo code, this obviously doesn't work)
SEL selector = selectorFromString(#"doWork");
[myobj respondsToSelector:selector];
I've been searching the Apple API docs, but haven't found a way that doesn't rely on the compile-time #selector(myTarget:) syntax.
I'm not an Objective-C programmer, merely a sympathizer, but maybe NSSelectorFromString is what you need. It's mentioned explicity in the Runtime Reference that you can use it to convert a string to a selector.
According to the XCode documentation, your psuedocode basically gets it right.
It’s most efficient to assign values to SEL variables at compile time with the #selector() directive. However, in some cases, a program may need to convert a character string to a selector at runtime. This can be done with the NSSelectorFromString function:
setWidthHeight = NSSelectorFromString(aBuffer);
Edit: Bummer, too slow. :P
I'd have to say that it's a little more complicated than the previous respondents' answers might suggest... if you indeed really want to create a selector... not just "call one" that you "have laying around"...
You need to create a function pointer that will be called by your "new" method.. so for a method like [self theMethod:(id)methodArg];, you'd write...
void (^impBlock)(id,id) = ^(id _self, id methodArg) {
[_self doSomethingWith:methodArg];
};
and then you need to generate the IMP block dynamically, this time, passing, "self", the SEL, and any arguments...
void(*impFunct)(id, SEL, id) = (void*) imp_implementationWithBlock(impBlock);
and add it to your class, along with an accurate method signature for the whole sucker (in this case "v#:#", void return, object caller, object argument)
class_addMethod(self.class, #selector(theMethod:), (IMP)impFunct, "v#:#");
You can see some good examples of this kind of runtime shenanigans, in one of my repos, here.
I know this has been answered for long ago, but still I wanna share. This can be done using sel_registerName too.
The example code in the question can be rewritten like this:
SEL selector = sel_registerName("doWork:");
[myobj respondsToSelector:selector];