69

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.

4 Answers 4

134

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.

Sign up to request clarification or add additional context in comments.

5 Comments

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.
However, is it possible to avoid the 'nil' termination thing? e.g. get the length of variable arguments?
@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.
@karim: Depending on your use case, you could instead give it a signature of doSomethingWith:(size_t) objects:(id), ...; this is in essence what stringWithFormat: 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 of nil termination, as the requirement to keep the count up to date could easily introduce defects.
@karim, your note in first comment is right. For handling the first argument you should assign format to arg, handle it, then continue in loop
23
- (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];

1 Comment

ARC version: NSString *s=[[NSString alloc] initWithFormat:format arguments:args];
12

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, @"");
}

Comments

0

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;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.