带有 FMDatabase 包装器的 SQLite 数据库

发布于 2024-11-03 14:06:14 字数 468 浏览 0 评论 0原文

我在 Objective C 中使用 FMDatabase SQLite 包装器,但遇到以下问题:

我正在后台线程中运行 XML 解析和数据库插入,以获取用户无权访问的某些内容,但用户可以与之交互它们所在部分的 UI 和数据库。

The FMDatabase <FMDatabase: 0x17b7b0> is currently in use.

随机地,我会收到“FMDatabase 已在使用中”通知,并且该数组永远不会由数据库填充。我的印象是 FMDatabase 类一旦空闲就会处理查询,但我有一个:

while(contents.count < 1){
     sleep(1);
}

希望一旦数据库空闲,数组就会被填充。如果数据库繁忙,我还尝试重新运行数组填充脚本,但无济于事。

抱歉,如果这个问题令人困惑,我很乐意澄清。

I am using the FMDatabase SQLite wrapper in Objective 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

給妳壹絲溫柔 2024-11-10 14:06:14

我遇到了同样的问题。

对于必须执行的每个数据库查询/更新,我都切换到 FMDatabaseQueue。它就像一个魅力!

使用performSelectorOnMainThread是一个好主意,但是当涉及到实际编码时,传递参数并获取结果以供进一步使用可能会非常棘手。

编辑:这是一个小例子

-(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;
}

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;
}
以酷 2024-11-10 14:06:14

您遇到此问题是因为您的应用程序是多线程的,并且您正在从不同的线程访问相同的 FMDatabase。类似的问题,但针对 Python,可以在此处找到。

FMDatabase 是 sqlite API 的包装器。 sqlite 默认情况下不允许并发,因此 FMDatabase 使用一个名为“inUse”的成员变量来跟踪。要解决您的问题,请尝试在 NSObject 上定义的这些方法之一,以便对 FMDatabase 的所有调用都发生在同一线程上。

  • PerformSelectorOnMainThread:withObject:waitUntilDone:
  • performSelectorOnMainThread:withObject:waitUntilDone:模式:
  • performSelector:onThread:withObject:waitUntilDone:
  • performSelector:onThread:withObject:waitUntilDone:模式:

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:
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文