How to create variable argument methods in Objective-C
Solution 1
What these are called, generally, is "variadic functions" (or methods, as it were).
To create this, simply end your method declartion with , ...
, as in
- (void)logMessage:(NSString *)message, ...;
At this point you probably want to wrap it in a printf
-like function, as implementing one of those from scratch is trying, at best.
- (void)logMessage:(NSString *)format, ... {
va_list args;
va_start(args, format);
NSLogv(format, args);
va_end(args);
}
Note the use of NSLogv
and not NSLog
; consider NSLog(NSString *, ...);
vs NSLogv(NSString *, va_list);
, or if you want a string; initWithFormat:arguments:
on NSString *
.
If, on the other hand, you are not working with strings, but rather something like
+ (NSArray *)arrayWithObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION;
things get a lot easier.
In that case, instead of a vprintf
-style function, use a loop going through args
, assuming id as you go, and parse them as you would in any loop.
- (void)logMessage:(NSString *)format, ... {
va_list args;
va_start(args, format);
id arg = nil;
while ((arg = va_arg(args,id))) {
/// Do your thing with arg here
}
va_end(args);
}
This last sample, of course, assumes that the va_args list is nil-terminated.
Note: In order to make this work you might have to include <stdarg.h>
; but if memory serves, this gets included in connection with NSLogv, meaning it comes down by way of "Foundation.h", therefore also "AppKit.h" and "Cocoa.h", as well as a number of others; so this should work out of the box.
Solution 2
- (void)methodWithFormat:(NSString*)format, ... {
va_list args;
va_start(args,format);
//loop, get every next arg by calling va_arg(args,<type>)
// e.g. NSString *arg=va_arg(args,NSString*) or int arg=(args,int)
va_end(args);
}
If you want to pass the variable arguments to stringWithFormat:, use something like:
NSString *s=[[[NSString alloc] initWithFormat:format arguments:args] autorelease];
Solution 3
One thing to mention here is that, the first NSString parameter here comes as format, and the other are passed in the variable argument. right? So before entering the for loop, you have one parameter to handle.
- (NSString *) append:(NSString *)list, ...
{
NSMutableString * res = [NSMutableString string];
[res appendString:list];
va_list args;
va_start(args, list);
id arg = nil;
while(( arg = va_arg(args, id))){
[res appendString:arg];
}
va_end(args);
return res;
}
- (void) test_va_arg
{
NSString * t = [self append:@"a", @"b", @"c", nil];
STAssertEqualObjects(@"abc", t, @"");
}
![Chris Rutkowski](https://i.stack.imgur.com/zcXZ0.jpg?s=256&g=1)
Chris Rutkowski
Updated on July 05, 2022Comments
-
Chris Rutkowski almost 2 years
Maybe this will be obviously simple for most of you, but could you please give an example how to create similar methods (in Objective-C) and functions in C to create functions like
NSString
'sstringWithFormat:
, orNSLog()
.Just to remind:
[NSString stringWithFormat:@"example tekst %i %@ %.2f", 122, @"sth", 3.1415"]; NSLog(@"account ID %i email %@", accountID, email);
I'd like to create the similar to
NSString
's methodstringWithFormat:
,NSURL - urlWithFormat
. -
karim over 10 yearsOne thing to mention here is that, the first NSString parameter here comes as format, and the other are passed in the variable argument. right? So before entering the for loop, you have one parameter to handle.
-
karim over 10 yearsHowever, is it possible to avoid the 'nil' termination thing? e.g. get the length of variable arguments?
-
newacct over 10 years@karim: It is not possible with C varargs to know the number and types of the arguments. The called function has to somehow know the types and when to stop.
-
Williham Totland over 10 years@karim: Depending on your use case, you could instead give it a signature of
doSomethingWith:(size_t) objects:(id), ...
; this is in essence whatstringWithFormat:
does (except that the number of expected arguments is obtained by dissecting the format string). Generally, tho', I'd advise against it, and in favor ofnil
termination, as the requirement to keep the count up to date could easily introduce defects. -
jackal over 10 years@karim, your note in first comment is right. For handling the first argument you should assign
format
toarg
, handle it, then continue in loop -
godzilla over 9 yearsARC version: NSString *s=[[NSString alloc] initWithFormat:format arguments:args];