views:

453

answers:

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 quries 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:?

+4  A: 

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.

+1  A: 

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
A: 

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
+3  A: 

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