Objective-C va_list and selectors - objective-c

Is it possible to use #selector and performSelector: (or similar) with methods using variable arguments list?
I'm writing a class that can be assigned a delegate to override the default behavior. In the presence of a delegate select method calls made on an instance of that class will be forward to the same corresponding delegate method, some which use variable argument lists.
So, for instance, I need to be able to create retrieve SEL reference and message the delegate object with a method such as this:
- (void)logEventWithFormat:(NSString *)format, ... {
va_list argList;
id del = self.delegate;
if (del != nil &&
[del conformsToProtocol:#protocol(AProtocolWithOptionalMethods)] &&
[del respondsToSelector:#selector(logEventWithFormat:)])
{
// Perform selector on object 'del' with 'argList'
}
}
I am assuming this is not possible, hence the similar method declaration in the Foundation framework - in NSString:
- (id)initWithFormat:(NSString*)format, ...;
and
- (id)initWithFormat:(NSString *)format arguments:(va_list)argList;
I assume that the protocol I wish to delegate to should suggest the implementation of:
- (void)logEventWithFormat:(NSString *)format arguments:(va_list)argList;
so I the selector #selector(logEventWithFormat:arguments:) can be used an called with:
[del performSelector:#selector(logEventWithFormat:arguments:)
withObject:format
withObject:argList];
I just wondered if I was missing something or going the long way around to achieve what I'm trying to?

You can pass anything you want into the runtime function objc_msgSend.
objc_msgSend(del, #selector(logEventWithFormat:arguments:), format, argList);
It's the most powerful way of sending a manually constructed message.
However, it's not clear that you need to perform the invocation this way. As KennyTM pointed out, in the code you have, you could invoke the method directly.

You can't use -performSelector:withObject:withObject: because va_list simply isn't an "object". You need to use NSInvocation.
Or simply call
[del logEventWithFormat:format arguments:argList];

As far as I know, it can't be done. You can't use -performSelector:withObject:withObject: because as #KennyTM points out, a va_list isn't an object.
However, you also cannot use NSInvocation. The documentation straight up says so:
NSInvocation does not support
invocations of methods with either
variable numbers of arguments or union
arguments.
Since these are the two ways of invoking a method by selector, and neither seems to work, I'm going to go with the "can't be done" answer, unless you invoke the method directly and pass the va_list as an argument.
Perhaps #bbum will show up and enlighten us further. =)

I haven't done it that way before, but the simple solution I've often used is to box/unbox either an NSMutableArray or an NSMutableDictionary for the withObject parameter.

Related

How to properly use makeObjectsPerformSelector: Getting error unrecognized selector

Let me start off by saying I am new to Objective C.
I am getting the error
atusMenuApp[24288:303] -[__NSCFConstantString createListItem]: unrecognized selector sent to instance 0x100002450
Here is my code:
selector = [NSMutableArray arrayWithObjects: #"nvda", #"aapl", #"goog", nil];
[selector makeObjectsPerformSelector:#selector(createListItem:) withObject:self];
- (void)createListItem:(NSString *)title {
//do some stuff
}
Now I have done plenty of looking around and it seems like the biggest reason for this issue is the addition of or lack of the :however I do believe I properly have that in place. Maybe I do not understand the use of makeObjectsPerformSelector very well as after look up the doc on it I found:
Sends to each object in the array the message identified by a given selector, starting with the first object and continuing through the array to the last object.
Any help would be great, Thanks!
[Only if you read the documentation (or thought a bit about why a method is named this way and not that), or even made the effort trying to understand the error message...]
The makeObjectsPerformSelector:withObject: method of NSArray does what it suggests it does: it makes the objects of the array perform the selector, that can have an optional argument. So
[selector makeObjectsPerformSelector:#selector(createListItem:) withObject:self];
will send the createListItem: message to every single NSString object in the selector array and pass in self as its argument. It won't perform the selector on self passing in the object. I. e., what you have is equivalent to
for (NSString *obj in selector) {
[obj createListItem:self];
}
Obviously, instead of this, you want the following:
for (NSString *obj in selector) {
[self createListItem:obj];
}
You don't even need that nasty method for this. A nice fast enumeration for loop will do it.
First you make an array of NSStrings. Then, you send them all the message createListItem. That's all fine and dandy, but NSString doesn't have any method called createListItem; just because you've defined an instance method called createListItem doesn't mean every instance of every class can use it. Only the class who's implementation file has the definition will be able to handle the message. For instance, I can't make a list of Car instances, then define the method fly in another class called Helicopter's implementation and expect to be able to call fly on an instance of Car; only Helicopter can use it. I recommend you read a good book on Objective-C and further familiarize yourself with classes, instances and instance methods.
You misunderstood the method.
It will call the method createListItem: with argument self over every object of the NSArray.
So the resulting call would be something like:
[#"nvda" createListItem:self];
...
Clearly that method doesn't exist for a NSString and there goes your exception.
If you need to apply a method of self to every object inside your array, simply loop through it.

Objective-C Selector pointer to be passed to a C function

I have a C struct that contains a function pointer. Now, I have used this setup within C with no problems, but now I'm using this C struct in Objective-C and I need to pass a function (or selector) pointer that is defined in the Objective-C class.
1. Here is what I have for the Objective-C selector that needs to be passed as a pointer to the C function:
- (void)myObjCSelector:(int*)myIntArray
{
// Do whatever I need with myIntArray
}
2. And here is where I run into a wall, Within Objective-C I'm trying to pass the selector as a pointer to the C function call: In place of "myObjCSelectorPointer" I need the proper syntax to pass the selector as a function pointer in this C function call:
passObjCSelectorPointerToCContext(cContextReference, myObjCSelectorPointer);
I did investigate this issue, but could mainly find several different ways of doing similar things, but I couldn't find anything specific for calling C functions and passing an Objective-C selector pointer.
In objc a selector is not a function pointer. A selector is a unique integer that is mapped to a string in a method lookup table stored by the objc runtime. In the above case your method name would be myObjCSelector: and to get the unique selector for it you would type #selector(myObjCSelector:). However this would be of no use to you because it doesnt represent a particular implementation of a function.
What youre looking for is IMP. Refer to this SO question.
EDIT 2:
IMP myObjCSelectorPointer = (void (*)(id,SEL,int*))[self methodForSelector:#selector(myObjCSelector:)];
Then you can call the method using
myObjCSelectorPointer(self,#selector(myObjCSelector:),myIntArray);
However, what this means you will need to make sure that you add the pointer to self in the c function call passObjCSelectorPointerToCContext.
So it should look like this
passObjCSelectorPointerToCContext(cContextReference, self, myObjCSelectorPointer);
when called from within the object that contains the method.
It is important to note though that using IMP is almost never the right technique. You should try to stick with pure Obj-C. Obj-C is quite efficient after the first call to a message because it uses temporal caching.
EDIT 1:
It's useful to understand why objc works in this way. The Apple documents explain it in depth. However a short explanation is as follows:
When you send a message to an object such as [myobject somemethod] the compiler won't immediately know which particular implementation of somemethod to call because there might be multiple classes with multiple overriden versions of somemethod. All of those methods have the same selector, irrespective of its arguments and return values and hence the decision about which implementation of somemethod is deffered to when the program is running. [myobject somemethod] gets converted by the compiler into a C function call:
objc_msgSend(myobject, #selector(somemethod))
This is a special function that searches each myobject class layout to see whether that class knows how to respond to a somemethod message. If not it then searches that class's parent and so on until the root. If none of the classes can respond to somemethod then NSObject defines a private method called forward where all unknown messages are sent.
Assuming that a class can respond to the somemethod message then it will also have a particular pointer of type IMP that points to the actual implementation of the method. At that point the method will be called.
There is considerably more to this procedure than I have described but the outline should be enough to help you understand what the goal of a selector is.
One final point is that the reason method names are mapped to unique integers via the #selector directive is so that the runtime doesn't have to waste time doing string comparisons.
Basically, the answer is: Objective-C selectors are different from function pointers. You need two pieces of data to perform a selector. That is an object and the selector itself. You will need some glue to accomplish your task.
Check this question.
Do you have to use a function pointer? In Objective-C, you can get the function pointer to an arbitrary method implementation (known as an IMP), but this is extremely uncommon, and usually not a good idea. Calling objc_msgSend() directly is also not the greatest idea, because there are several different variants of objc_msgSend(), and the compiler automatically chooses different ones to use based on the return type of the method. Methods that return an object go through objc_msgSend(), but objects that return structs might go through objc_msgSend() or they might go through objc_msgSend_stret(). And if the method returns a double, then it goes through objc_msgSend_fpret()...
Documentation: Objective-C Runtime Reference: Sending Messages
Instead, I might recommend using a target-action pair, or using a block. Then you might do something like:
myContextRef->target = anObjcObject;
myContextRef->action = #selector(invokeMe:);
And when you're done, do:
[myContextRef->target performSelector:myContextRef->action withObject:someReturnInformation];
Or maybe use a block:
myContextRef->completionHandler = [^(id returnInformation) {
[anObjcObject invokeMe:returnInformation];
} copy];
And then when you're done, do:
myContextRef->completionHandler(someReturnInformation);
(and don't forget to -release the block when you free the context)

Calling Objective-C methods

Let's say I have a method called foo. What's the difference between:
[self foo];
and
[self performSelector:#selector(foo)];
Are they the same? The first one seems so much easier, so why would you ever want to use the second one?
From the docs:
The performSelector: method is equivalent to sending an aSelector message directly to the receiver. For example, all three of the following messages do the same thing:
id myClone = [anObject copy];
id myClone = [anObject performSelector:#selector(copy)];
id myClone = [anObject performSelector:sel_getUid("copy")];
However, the performSelector: method allows you to send messages that aren’t determined until runtime. A variable selector can be passed as the argument:
SEL myMethod = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector:myMethod];
Well, say you were getting a SEL from somewhere else, and you just wanted to execute it, you'd use this method.
Otherwise, yeah, you'd usually want to use the first example.
The first sends a message to an object and so does the second. In almost all cases, you should use the first. It's quicker and clearer. However, the second has its uses. For instance, you can use it in a situation where you would need to supply a call back.
Another powerful use is in combination with NSSelectorFromString(). You can literally decide what message to use at run time based on a string in a configuration file or from user input (don't forget to validate it!). You can even build selector names using NSString -stringWithFormat: etc. For instance, this parser kit uses the technique to inform the program when parser rules have been matched.

How to call a method with parameters using #selector

I have two methods with same name but different parameters say:
-(void)methodA:(NSString*)string
-(void)methodA:(NSNotification*)notification
Now i need to call those methods using #selector with the parameters. How to do it ?
SEL aSel = #selector(methodA:);
[objectTakesString performSelector:aSel withObject:#"A string!"];
[objectTakesNtfctn performSelector:aSel withObject:[NSNotification notificationWith...]];
-[NSObject performSelector:withObject:] takes both the selector to be invoked, and a pointer to the parameter to pass it.
In my opinion, both the preceding answers are wrong. If you use
-[NSObject performSelector:withObject:];
as Sixten Otto suggests, you would have to place this at the beginning of your method:
-(void)methodA:(id)stringOrNotification
{
if ([stringOrNotification isKindOfClass:[NSString class])
{
..do something..
}
if ([stringOrNotification isKindOfClass:[NSNotification class])
{
..do something else..
}
. . .
}
Apart from this (bad) approach, what you're asking for can't in fact (easily) be done in Objective-C, because the language doesn't support parametric polymorphism. In other words, the type information is not used in the message dispatch. Yet another way to say this is that there can't be "two methods with same name but different parameters" in the same class. In fact, I'm surprised that you haven't yet seen a compiler error if you tried to declare this.
(If you've got the two methods defined on different classes, as dreamlax seems to assume, the message invocation will work trivially, even if you don't use performSelector:. Like so:
id eitherStringOrNotification;
[objectOfUncertainClass methodA:eitherStringOrNotification];
If that's what you're asking for, you don't have a problem.
)
Note that the NSMethodSignature object does contain information about the parameter types, but it's generated by the receiving object, so you can't really use it to distinguish between messages based on what parameter types are handed in (since that information is not available when the method signature is instantiated).

Using -performSelector: vs. just calling the method

I'm still kind of new to Objective-C and I'm wondering what is the difference between the following two statements?
[object performSelector:#selector(doSomething)];
[object doSomething];
Basically performSelector allows you to dynamically determine which selector to call a selector on the given object. In other words the selector need not be determined before runtime.
Thus even though these are equivalent:
[anObject aMethod];
[anObject performSelector:#selector(aMethod)];
The second form allows you to do this:
SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];
before you send the message.
For this very basic example in the question,
[object doSomething];
[object performSelector:#selector(doSomething)];
there is no difference in what is going to happen. doSomething will be synchronously executed by object. Only "doSomething" is a very simple method, that does not return anything, and does not require any parameters.
were it something a little more complicated, like:
(void)doSomethingWithMyAge:(NSUInteger)age;
things would get complicated, because
[object doSomethingWithMyAge:42];
can no longer be called with any variant of "performSelector", because all variants with parameters only accept object parameters.
The selector here would be "doSomethingWithMyAge:" but any attempt to
[object performSelector:#selector(doSomethingWithMyAge:) withObject:42];
simply won't compile. passing an NSNumber: #(42) instead of 42, wouldn't help either, because the method expects a basic C type - not an object.
In addition, there are performSelector variants up to 2 parameters, no more. While methods many times have many more parameters.
I have found out that although synchronous variants of performSelector:
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
always return an object, I was able to return a simple BOOL or NSUInteger too, and it worked.
One of the two main uses of performSelector is to compose dynamically the name of the method you want to execute, as explained in a previous answer. For example
SEL method = NSSelectorFromString([NSString stringWithFormat:#"doSomethingWithMy%#:", #"Age");
[object performSelector:method];
The other use, is to asynchronously dispatch a message to object, that will be executed later on the current runloop. For this, there are several other performSelector variants.
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
- (void)performSelector:(SEL)aSelector target:(id)target argument:(id)arg order:(NSUInteger)order modes:(NSArray *)modes;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;
(yes I gathered them from several Foundation class categories, like NSThread, NSRunLoop and NSObject)
Each of the variants has its own special behavior, but all share something in common (at least when waitUntilDone is set to NO). The "performSelector" call would return immediately, and the message to object will only be put on the current runloop after some time.
Because of the delayed execution - naturally no return value is available form the method of the selector, hence the -(void) return value in all these asynchronous variants.
I hope I covered this somehow...
#ennuikiller is spot on. Basically, dynamically-generated selectors are useful for when you don't (and usually can't possibly) know the name of the method you'll be calling when you compile the code.
One key difference is that -performSelector: and friends (including the multi-threaded and delayed variants) are somewhat limited in that they are designed for use with methods with 0-2 parameters. For example, calling -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation: with 6 parameters and returning the NSString is pretty unwieldy, and not supported by the provided methods.
Selectors are a bit like function pointers in other languages. You use them when you don't know at compile time which method you want to call at runtime. Also, like function pointers, they only encapsulate the verb part of invocation. If the method has parameters, you will need to pass them as well.
An NSInvocation serves a similar purpose, except that it binds together more information. Not only does it include the verb part, it also includes the target object and the parameters. This is useful when you want to call a method on a particular object with particular parameters, not now but in the future. You can build an appropriate NSInvocation and fire it later.
There is another subtle difference between the two.
[object doSomething]; // is executed right away
[object performSelector:#selector(doSomething)]; // gets executed at the next runloop
Here is the excerpt from Apple Documentation
"performSelector:withObject:afterDelay:
Performs the specified selector on the current thread during the next run loop cycle and after an optional delay period. Because it waits until the next run loop cycle to perform the selector, these methods provide an automatic mini delay from the currently executing code. Multiple queued selectors are performed one after another in the order they were queued."