Таинственный EXC_BAD_ACCESS без зомби или неинициализированных переменных

Мое приложение вылетает из следующей строки:

sqlite3_prepare_v2(db, [sql UTF8String], -1, &pStmt, 0);

в методе оболочки sqlite FMDB:

- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args {

if (![self databaseExists]) {
    return 0x00;
}

if (inUse) {
    [self warnInUse];
    return 0x00;
}

[self setInUse:YES];

FMResultSet *rs = nil;

int rc                  = 0x00;
sqlite3_stmt *pStmt     = 0x00;
FMStatement *statement  = 0x00;

if (traceExecution && sql) {
    NSLog(@"%@ executeQuery: %@", self, sql);
}

if (shouldCacheStatements) {
    statement = [self cachedStatementForQuery:sql];
    pStmt = statement ? [statement statement] : 0x00;
}

int numberOfRetries = 0;
BOOL retry          = NO;

if (!pStmt) {
    do {
        retry   = NO;
        const char *sqlStatement = [sql UTF8String];
        rc      = sqlite3_prepare_v2(db, sqlStatement, -1, &pStmt, 0);

        if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
            retry = YES;
            usleep(20);

            if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
                NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
                NSLog(@"Database busy");
                sqlite3_finalize(pStmt);
                [self setInUse:NO];
                return nil;
            }
        }
        else if (SQLITE_OK != rc) {


            if (logsErrors) {
                NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
                NSLog(@"DB Query: %@", sql);
#ifndef NS_BLOCK_ASSERTIONS
                if (crashOnErrors) {
                    NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
                }
#endif
            }

            sqlite3_finalize(pStmt);

            [self setInUse:NO];
            return nil;
        }
    }
    while (retry);
}

id obj;
int idx = 0;
int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)

while (idx < queryCount) {

    if (arrayArgs) {
        obj = [arrayArgs objectAtIndex:idx];
    }
    else {
        obj = va_arg(args, id);
    }

    if (traceExecution) {
        NSLog(@"obj: %@", obj);
    }

    idx++;

    [self bindObject:obj toColumn:idx inStatement:pStmt];
}

if (idx != queryCount) {
    NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
    sqlite3_finalize(pStmt);
    [self setInUse:NO];
    return nil;
}

[statement retain]; // to balance the release below

if (!statement) {
    statement = [[FMStatement alloc] init];
    [statement setStatement:pStmt];

    if (shouldCacheStatements) {
        [self setCachedStatement:statement forQuery:sql];
    }
}

// the statement gets closed in rs's dealloc or [rs close];
rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self];
[rs setQuery:sql];
NSValue *openResultSet = [NSValue valueWithNonretainedObject:rs];
[openResultSets addObject:openResultSet];

statement.useCount = statement.useCount + 1;

[statement release];    

[self setInUse:NO];

return rs;

}

Приложение вылетает из-за EXC_BAD_ACCESS. Я пытался выяснить, почему, отлаживая с помощью NSZombieEnabled и malloc_history, но это не дает мне никаких ответов. Кроме того, отладчик сообщает мне, что в переменной sql имеется очень большое количество сохраненных значений (что, вероятно, связано с тем, что это статический NSString), поэтому EXC_BAD_ACCESS не должен быть из-за чрезмерного освобождения объекта sql.

Есть ли у кого-нибудь идеи, как продолжить отладку, чтобы выяснить, в чем проблема?

7
задан Piotr 23 June 2011 в 14:15
поделиться