Reputation: 1814
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
's stringWithFormat:
, or NSLog()
.
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 method stringWithFormat:
, NSURL - urlWithFormat
.
Upvotes: 69
Views: 46888
Reputation: 607
Here is GNUStep's implementation
#import <Foundation/Foundation.h>
#define GS_MAX_OBJECTS_FROM_STACK 128
#define GS_USEIDLIST(firstObject, code...) ({\
va_list __ap; \
unsigned int __max = GS_MAX_OBJECTS_FROM_STACK; \
unsigned int __count = 0; \
id __buf[__max]; \
id *__objects = __buf; \
id __obj = firstObject; \
va_start(__ap, firstObject); \
while (__obj != nil && __count < __max) \
{ \
__objects[__count] = __obj; \
__obj = va_arg(__ap, id); \
if (++__count == __max) \
{ \
while (__obj != nil) \
{ \
__count++; \
__obj = va_arg(__ap, id); \
} \
} \
} \
va_end(__ap); \
if (__count > __max) \
{ \
unsigned int __tmp; \
__objects = (id*)NSZoneMalloc(NSDefaultMallocZone(),__count*sizeof(id)); \
va_start(__ap, firstObject); \
__objects[0] = firstObject; \
for (__tmp = 1; __tmp < __count; __tmp++) \
{ \
__objects[__tmp] = va_arg(__ap, id); \
} \
va_end(__ap); \
} \
code; \
if (__objects != __buf) NSZoneFree (NSDefaultMallocZone(),__objects); \
})
@interface MyObject : NSObject
- (void)printWithObjects:(id)firstObject, ... NS_REQUIRES_NIL_TERMINATION;
@end
@implementation MyObject
- (void)printWithObjects:(id)firstObject, ... {
GS_USEIDLIST(firstObject, {
for (unsigned int i = 0; i < __count; i++) {
NSLog(@"%@", __objects[i]);
}
});
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
[[MyObject new] printWithObjects:@"1", @"2", @"3", nil];
}
return 0;
}
Upvotes: 0
Reputation: 15589
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, @"");
}
Upvotes: 12
Reputation: 29009
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.
Upvotes: 134
Reputation: 370
- (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];
Upvotes: 23