How can I fix a cast of an indirect pointer (under ARC) in this code?-Collection of common programming errors
So we’re trying to walkthru a SQLite example which implements NSFastEnumeration, provided by Lynda.com. Converting it to ARC has been an issue for me as I have been unable to fix the error
Cast of an indirect pointer to an Objective-C pointer to 'va_list' (aka char *) is disallowed with ARC.
It is pointing to this line and the va_list
cast
[self bindSQL:[query UTF8String] arguments:(va_list)values];
which is in this function where values
is defined (__unsafe_unretained
was added to fix some other error)
- (NSNumber *) insertRow:(NSDictionary *) record
{
int dictSize = [record count];
__unsafe_unretained id values[dictSize];
The implementation of bindSQL is
- (void) bindSQL:(const char *) cQuery arguments:(va_list)args
How is this corrected?
Thanks for the interest guys. As requested here is the original bindSQL function and the calling insertRow function
- (void) bindSQL:(const char *) cQuery arguments:(va_list)args {
// NSLog(@"%s: %s", __FUNCTION__, cQuery);
int param_count;
// 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(@"bindSQL: could not prepare statement (%s)", sqlite3_errmsg(database));
statement = NULL;
return;
}
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 == nil) {
sqlite3_bind_null(statement, i + 1);
} else 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(@"bindSQL: Unhandled objCType: %s", [o objCType]);
statement = NULL;
return;
}
} else if ([o respondsToSelector:@selector(UTF8String)]) { // string
sqlite3_bind_text(statement, i + 1, [o UTF8String], -1, SQLITE_TRANSIENT);
} else { // unhhandled type
NSLog(@"bindSQL: Unhandled parameter type: %@", [o class]);
statement = NULL;
return;
}
}
}
va_end(args);
return;
}
- (NSNumber *) insertRow:(NSDictionary *) record {
// NSLog(@"%s", __FUNCTION__);
int dictSize = [record count];
// the values array is used as the argument list for bindSQL
id keys[dictSize]; // not used, just a side-effect of getObjects:andKeys
id values[dictSize];
[record getObjects:values andKeys:keys]; // convenient for the C array
// construct the query
NSMutableArray * placeHoldersArray = [NSMutableArray arrayWithCapacity:dictSize];
for (int i = 0; i < dictSize; i++) // array of ? markers for placeholders in query
[placeHoldersArray addObject: [NSString stringWithString:@"?"]];
NSString * query = [NSString stringWithFormat:@"insert into %@ (%@) values (%@)",
tableName,
[[record allKeys] componentsJoinedByString:@","],
[placeHoldersArray componentsJoinedByString:@","]];
[self bindSQL:[query UTF8String] arguments:(va_list)values];
sqlite3_step(statement);
if(sqlite3_finalize(statement) == SQLITE_OK) {
return [self lastInsertId];
} else {
NSLog(@"doQuery: sqlite3_finalize failed (%s)", sqlite3_errmsg(database));
return [NSNumber numberWithInt:0];
}
}
-
It seems that your
bindSQL:arguments:
acceptsva_list
while you are passing an array ofid
‘s, instead.Therefore, ARC does not know how to deal with cast from
id*
tova_list
. If it worked before turning on ARC, it was just you being lucky that some internal representation at your target machine matched. But you cannot be sure of it even without ARC as it can start giving you, unexpectedly, some nasty crashes. -
Craig,
Why are you using a va_list at all? As you pull each individual item out of the list, you could easily just leave the data in the NSDictionary and pull them out with an -objectAtIndex: applied to the -allValues array. Then you’ve just avoided this problem altogether.
Andrew
P.S. Don’t fight the framework or runtime. You’ll be frustrated and will lose.
Craig,
I’m not sure what is wrong with your code but I was interested enough to write an example that does compile. I hope this helps you figure your problem out. My example is a traditional application of a va_list in a logging example. (This code was extracted from my more complex logging macros. Hence it primarily tests the language and doesn’t do anything novel.)
First, the class:
@interface Macros + (void) testWithVAList: (va_list) vaList; + (void) callTestWithVAList: (va_list) vaList; @end @implementation Macros + (void) testWithVAList: (va_list) vaList { } // -testWithVAList: + (void) callTestWithVAList: (va_list) vaList { [self testWithVAList: vaList]; } // -callTestWithVAList: @end
Now a standard C-function that calls the class:
void testLog(NSString *format, ...); void testLog(NSString *format, ...) { va_list argp; va_start(argp, format); [Macros testWithVAList: argp]; NSLogv(format, argp); va_end(argp); } // testLog()
This code compiles just fine in Xcode v4.2.1 using clang v3.
Notice that there are no
__bridge
or any other ARC type qualifiers for this native C-type. Hence, I don’t know what is wrong with your code. (Frankly, you didn’t give us enough code to really help you solve your problem.)In conclusion, a
va_list
is compatible with ARC. It is an independent of ARC type.Andrew