Variable Arguments (varargs) in Objective-C

Creating variable argument functions (also vararg or variadic function) in Objective-C is done the same way that it is done in C, using the stdarg.h library.

For those of us working in Cocoa, Apple has already included the import in NSObjCRuntime.h, so the only thing we have to do is start using it.
As an example, let’s take a function that adds an arbitrary list of doubles stored in NSNumbers:

This is the function definition, we should see this copied exactly in the header file.

The ellipse (…) indicates that this function can accept an arbitrary number of arguments, and before that we are required to have at least one argument of any type. We must somehow know the count to keep from dereferencing an invalid pointer, but sometimes it is possible to infer that count from one of the other arguments (e.g., in NSLog or printf by counting the number of unescaped % symbols), or in NSMutableArray by having the user terminate the sequence with nil.

If we used that last approach here, we would change the function to:- (NSNumber *) addValues:(NSNumber *) firstNumber, …

It could then be called as:addValues: num1, num2, num3, nil instead of addValues: 3, num1, num2, num3.

Line 2: va_list args;

va_list is of type void *, so it acts as an arbitrary array of objects.

Line 3: va_start(args, count);

Indicates that we will be using args to store the variable argument list, and that count is the last “fixed” parameter. This indicates where the compiler needs to go in memory to find the start of args.

Line 9: for( int i = 0; i < count; i++ )

A standard for loop, note that we cannot infer the size of args and have to explicitly know the count.

If we were using a nil terminated list, we could use:

while( value =va_arg( args, NSNumber*))

instead of Line 9 and Line 10 here.

Line 10: value = va_arg(args, NSNumber *);

Stores the next argument from args in value, casting it appropriately to a NSNumber * (for maximum flexibility, use id).

Line 16: va_end(args);

Close off the variable argument list args after using it.

Precaution

If you are using va_arg(args, double) (or float or another raw type like that), you may get erratic results if you attempt to pass an array of integers in, for example: addValues: 4, 4, 3, 2, 1. It would work, however, if you explicitly declared each of those variables as a double (e.g., double num1, double num2, double num3, double num4.

This is because if the compiler sees that it is a int going into a function that explicitly has a double argument it can cast it before sending it along. It doesn’t know what type to use with a variable argument list, so it assumes that you know what you are doing and so it just passes the integer along to the other side.