I am using the FMDatabase SQLite wrapper in Object开发者_高级运维ive C and I have the following issue:
I am running an XML parse and DB insert in a background thread for some content that the user does not have access to, however the user is able to interact with a UI and database from the section they are in.
The FMDatabase <FMDatabase: 0x17b7b0> is currently in use.
Randomly, I will get a "FMDatabase already in use" notification and the array will never be populated by the database. I was under the impression that the FMDatabase class would handle the query once it became free, but I have a:
while(contents.count < 1){
sleep(1);
}
Hoping that once the database frees up, the array will be populated. I have also tried rerunning the array population script if the DB is busy but to no avail.
Sorry if this question is confusing, I am happy to clarify.
I experienced the same issue.
I switched to FMDatabaseQueue
for every database query/update I had to do. It works like a charm !
Using performSelectorOnMainThread
is a good idea but when it comes to actual coding it can be pretty tricky to pass your arguments and get the results for further use.
EDIT : here is a little example
-(void) updateTaskStateAsync:(NSNumber *)taskID withNewStatus:(NSNumber *)state andCompletionBlock:(void(^)(BOOL status))completionBlock{
NSString *errInfo = [NSString stringWithFormat:@"taskID %d - state %d", [taskID intValue], [state intValue]];
[queue inDatabase:^(FMDatabase *db) {
BOOL r = [db executeUpdate:@"UPDATE tasks SET state=?, date_job_last_updated=? WHERE identifier=?", state, [NSDate dateWithTimeIntervalSinceNow:0], taskID];
DB_DISPLAY_ERROR(errInfo); // convenient macro to log errors
if(completionBlock)
completionBlock(r);
}];
}
-(BOOL) updateTaskStateSync:(NSNumber *)taskID withNewStatus:(NSNumber *)state {
NSString *errInfo = [NSString stringWithFormat:@"taskID %d - state %d", [taskID intValue], [state intValue]];
__block BOOL r = NO;
__block BOOL ended = NO;
[queue inDatabase:^(FMDatabase *db) {
r = [db executeUpdate:@"UPDATE tasks SET state=?, date_job_last_updated=? WHERE identifier=?", state, [NSDate dateWithTimeIntervalSinceNow:0], taskID];
DB_DISPLAY_ERROR(errInfo); // convenient macro to log errors
ended = YES;
}];
NSCondition *cond = [[NSCondition alloc] init];
[cond lock];
while(!ended)
[cond wait];
[cond unlock];
return r;
}
You're hitting this issue because your application is multi-threaded and you're accessing the same FMDatabase from different threads. A similar question, but for Python, can be found here.
FMDatabase is a wrapper around the sqlite API. sqlite by default does not allow for concurrency, so FMDatabase uses an member variable, called "inUse", to track. To fix your issue, try one of these methods defined on NSObject so that all calls to FMDatabase occur on the same thread.
- performSelectorOnMainThread:withObject:waitUntilDone:
- performSelectorOnMainThread:withObject:waitUntilDone:modes:
- performSelector:onThread:withObject:waitUntilDone:
- performSelector:onThread:withObject:waitUntilDone:modes:
精彩评论