Simulating dynamic typing in Objective C
Objective C implements polymorphism by using a generic object pointer type (id) and by requiring methods that provide information about the capabilities of a class implementation.
Objective C’s NSObject superclass implements methods like isMemberOfClass and respondsToSelector. These methods allow us to simulate dynamic typing by inspecting an object to see what it can do. It’s important to draw a distinction between what a class is and what it can do. The idea of polymorphism is to use a class without concern for the exact definition of that class, as long as it can do what you need it to do.
Recently, I was playing with SQLite under iOS and I decided to write a generic wrapper that could do something like this:
- (NSNumber *) SQLdo:(NSString *) query, ...;
This method would allow me to write arbitrary SQL queries with arbitrary numbers and types of parameters, using C’s variadic parameter system for the list of parameters. The implementation looks like this:
// SQLdo:query,...
// executes a non-select query on the SQLite database
// bound parameters are passed in the variadic argument list
// objects in variadic list are tested for type
// Return value is the number of affect rows
- (NSNumber *) SQLdo:(NSString *) query, ... {
int param_count;
// NSLog(@"%s: %@", __FUNCTION__, query);
va_list args;
va_start(args, query);
[self openDB];
const char *cQuery = [query UTF8String];
sqlite3_stmt *statement;
// preparing the query here allows SQLite to determine
// the number of required parameters
if (sqlite3_prepare_v2(database, cQuery, -1, &statement, NULL)
!= SQLITE_OK) {
NSLog(@"SQLdo: could not prepare statement (%s)",
sqlite3_errmsg(database));
return [NSNumber numberWithInt:0];
}
if ((param_count = sqlite3_bind_parameter_count(statement))) {
for (int i = 0; i < param_count; i++) {
id o = va_arg(args, id);
// determine the type of the argument
if ([o respondsToSelector:@selector(objCType)]) {
if (strchr("islISLB", *[o objCType])) { // integer
sqlite3_bind_int(statement, i + 1, [o intValue]);
} else if (strchr("fd", *[o objCType])) { // double
sqlite3_bind_double(statement, i + 1, [o doubleValue]);
} else { // unhandled types
NSLog(@"SQLdo: Unhandled objCType: %s", [o objCType]);
return [NSNumber numberWithInt:0];
}
} else if ([o respondsToSelector:@selector(UTF8String)]) {
// string
sqlite3_bind_text(statement, i + 1, [o UTF8String], -1,
SQLITE_TRANSIENT);
} else { // unhhandled type
NSLog(@"SQLdo: Unhandled parameter type: %@", [o class]);
return [NSNumber numberWithInt:0];
}
}
}
va_end(args);
sqlite3_step(statement);
if(sqlite3_finalize(statement) == SQLITE_OK) {
return [NSNumber numberWithInt: sqlite3_changes(database)];
} else {
NSLog(@"SQLdo: sqlite3_finalize failed (%s)",
sqlite3_errmsg(database));
return [NSNumber numberWithInt:0];
}
}
Notice the for loop for processing the variadic list. va_arg() is used to get the next object using Objective C’s generic object pointer type, id. This is then tested using respondsToSelector to find out if it’s numeric. All the numeric types respond to the objCType selector. The UTF8String selector is used for providing SQLite’s TEXT type.
This is a really useful paradigm, and it was relatively easy to implement using Objective C. I’m looking forward to extending it while I build a generic SQLite wrapper for my iOS apps.
Nice posting…..
thank you for this precious contribution
but i didn’t find out why you search islISLB in the NSString returned by objCType and how may i include dates in the binding?