4

Some context: I'm trying to clean up some of my FMDB code. My one table has a lot of columns and the method in FMDB I need to use is one that expects a variable number of arguments, similar to NSString's class method +stringWithFormat:.

An example:

[db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,
@"hi'", // look!  I put in a ', and I'm not escaping it!
[NSString stringWithFormat:@"number %d", i],
[NSNumber numberWithInt:i],
[NSDate date],
[NSNumber numberWithFloat:2.2f]];

When a table only has 5 columns it's not that bad but when a column has 20+ it starts to get hairy.

What I'd like to do is create a dictionary with all db abstraction information and build these queries dynamically. My question is... How in Objective-C do I fake out that method expecting a variable number of arguments and instead perhaps hand it an NSArray?

Related info:

How can I write a method that takes a variable number of arguments, like NSString's +stringWithFormat:?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
zorn
  • 438
  • 1
  • 5
  • 15
  • Unfortunately, no. Here's the same question from a couple of months ago: http://stackoverflow.com/questions/431910/is-possible-send-a-array-in-obj-c-for-a-variable-arguments-function – Chuck Mar 27 '09 at 00:30

4 Answers4

6

(Edit: This worked back in the GCC days. It doesn't under Clang as of Xcode 4.6.)


Get the objects in the array into a C array, then treat that as a varargs list:

//The example input array
int i = 42;
NSArray *array = [NSArray arrayWithObjects:
    [NSString stringWithFormat:@"number %d", i],
    [NSNumber numberWithInt:i],
    [NSDate date],
    [NSNumber numberWithFloat:2.2f],
    nil];

//The example destination (using NSString so anyone can test this)
NSString *string = nil;

//The intermediary C array
NSObject **arrayObjects = malloc(sizeof(NSObject *) * [array count]);
if (arrayObjects) {
    //Fill out the C array.
    [array getObjects:arrayObjects];

    //Use the C array as a va_list.
    string = [[[NSString alloc] initWithFormat:@"%@ %@ %@ %@" arguments:(va_list)arrayObjects] autorelease];

    free(arrayObjects);
}

NSLog(@"string: %@", string);

Output:

2009-03-26 20:10:07.128 NSArray-varargs[606:10b] string: number 42 42 2009-03-26 20:10:07 -0700 2.2

In your case, you'll use the -[FMDatabase executeUpdate:arguments:] method.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • I'm trying to do the same thing with cocos2d iphone framework and the array data is being copied correctly, but when I pass the `(va_list)dyn_va_list` to the method expecting a va list, it doesn't setup its `va_list params;` variable with a valid address and I get EXC_BAD_ACCESS. I'm losing my mind, please help. Thanks. – Brenden Dec 03 '10 at 21:58
  • I found that a dynamic va_list did not work in this case without changing the class method's va_start to va_copy. Not sure how it would work for any other class, but so it goes. (further reading: http://www.cocos2d-iphone.org/forum/topic/2547#post-65713) – Brenden Dec 04 '10 at 00:24
  • Brenden: If it's not a valid C array of the objects you need to format into the string, then that won't work. You should ask a full question complete with an excerpt of your code including an example of the items in the array and the format string. – Peter Hosey Dec 04 '10 at 05:59
  • This is the correct solution. I found a good explanation with clear code samples here: http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html – Tatiana Racheva Jul 10 '11 at 01:18
  • 1
    Smartish. But had to break eventually, since it relies on undocumented behavior. Xcode 4.6 now won't compile it: `error: used type 'va_list' (aka '__builtin_va_list') where arithmetic or pointer type is required` – Gwendal Roué Feb 02 '13 at 08:00
4

It might be easier to just make a category on FMDatabase that takes an array and does the updates. You should be able to copy most of executeUpdate to do it.

ccgus
  • 2,906
  • 23
  • 18
1

I think NSInvocation may do what you're looking to do.

Just be careful when calling setArgumentForIndex because args 0 and 1 are implicit ones that Obj-C fills in, where arg 2 is the first "real" arg that you're passing.

  • That's like using a hammer to crack a nut. There are much better solutions than this one. I wont vote this one done because it _is_ a solution, just IMHO not a very good one. – schwa Mar 27 '09 at 00:49
  • 2
    From the documentation: "NSInvocation does not support invocations of methods with either variable numbers of arguments or union arguments." – Gwendal Roué Feb 02 '13 at 07:58
0

This may not be the example you're looking for. But in this case I'd put your string values into an array and then use [theArray componentsJoinedByString:@","] to turn them into your sql argument list.

schwa
  • 11,962
  • 14
  • 43
  • 54