views:

443

answers:

4

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?

A: 

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.

David Sowsy
+1  A: 

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];
KennyTM
`NSInvocation` will not work. The documentation says so.
Dave DeLong
@Dave: `NSInvocation` *will* work if you pass the `va_list` as an argument (`va_list` is equivalent to `void*` in the eye of the ObjC runtime). You just can't pass arbitrary many arguments to e.g. `-stringWithFormat:` with `NSInvocation` because the type signature omits the `...` part.
KennyTM
A: 

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. =)

Dave DeLong
+1  A: 

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.

Matt Gallagher