Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>There's no portable way to build va_lists in C, C++, or Objective-C. You could probably write or find some functions that use inline assembly to modify the stack directly and make variadic calls, but that's really not a good way to go. You have three practical options that I can think of.</p> <p>Here's the first option. Use mutable strings only and forward the arguments using NSString's initWithFormat:arguments: method.</p> <pre><code>- (NSString*)forwardMessage:(NSString*)format, ... { va_list args; va_start(args, format); BOOL escape = NO; char* ptr = (char*)[format UTF8String]; while (*ptr) { if (*ptr == '%') { escape = !escape; } else if (escape) { // argument id obj = va_arg(args, id); if (*ptr == '@') { // object if ([obj isKindOfClass:[NSString class]]) { // string id copy = [obj copy]; if (copy != obj) { // mutable [obj replaceCharactersInRange:NSMakeRange(0, [obj length]) withString:@"replaced!"]; } } } escape = NO; } ++ptr; } va_end(args); va_list args2; va_start(args2, format); NSString* ret = [[NSString alloc] initWithFormat:format arguments:args2]; va_end(args2); return ret; } </code></pre> <p>That method will take variadic arguments and will replace the contents of any mutable string with "replaced!". Since we can only read the arguments before forwarding them, we can't actually send different objects to the initWithFormat:arguments: we just have to alter the objects. Be aware that making a copy of the object to test whether or not it's mutable like I did in that method isn't great practice.</p> <p>Here's your second option. Use NSInvocation to build your new arguments to stringWithFormat:.</p> <pre><code>- (NSString*)forwardMessage:(NSString*)format, ... { BOOL escape = NO; NSUInteger count = 0; char* ptr = (char*)[format UTF8String]; while (*ptr) { if (*ptr == '%') { escape = !escape; } else if (escape) { if (*ptr == '@') { // this is an object } ++count; escape = NO; } ++ptr; } char* sig = malloc(3 + count + 2); memset(sig, '@', 3 + count); sig[3 + count] = ':'; sig[3 + count + 1] = '\0'; NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:sig]]; free(sig); [invocation setTarget:[NSString class]]; [invocation setSelector:@selector(stringWithFormat:)]; [invocation setArgument:&amp;format atIndex:2]; va_list args; va_start(args, format); for (NSUInteger i = 0; i &lt; count; ++i) { void* arg = va_arg(args, void*); // arg is an object, you can change it here [invocation setArgument:&amp;arg atIndex:i + 3]; } [invocation invoke]; va_end(args); id ret; [invocation getReturnValue:&amp;ret]; return ret; } </code></pre> <p>That method does have one drawback: It won't work when you pass types that have sizes that aren't the same as void* or id. For example, integers work, but floats don't come out right. <code>[self forwardMessage:@"test %d asd %s %f %@ %d", 2, "asd", 2.897, @"test" 5]</code> returns <code>test 2 asd asd 0.000000 test 5</code>. It wouldn't be too hard to add a bit more logic to make specific types work correctly though.</p> <p>I'm not sure I can really recommend either of these solutions, but maybe they'll work for your situation.</p> <p>The third option: If your arguments are only going to be NSString objects, I'm gonna have to recommend abandoning stringWithFormat: and parsing / forming the formatted string yourself.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload