美文网首页iOS开发专题
关于FMDB报databaseislocked的解决方案

关于FMDB报databaseislocked的解决方案

作者: 老南 | 来源:发表于2016-08-17 15:56 被阅读309次

    最近做一个二手项目,首页使用FMDB初始化了很多数据,之后应为需求需要,又加了很多初始化数据.

    FMDB单例的写法是这样的:

    + (IMCache *)shareIMCache

    {

    static IMCache *instance = nil;

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

    instance = [[IMCache alloc] init];

    });

    return instance;

    }

    - (instancetype)init

    {

    self = [super init];

    if (self) {

    NSString * dbPath = [PATH_USER_DOCUMENT stringByAppendingPathComponent:@"imcache.db"];

    dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath];

    [dbQueue inDatabase:^(FMDatabase *db) {

    [db executeUpdate:[IMCache createMessageTableSql]];

    [db executeUpdate:[IMCache createContectTableSql]];

    }];

    }

    return self;

    }

    把数据库封装成了一个单例,添加增删改查等常用方法

    使用FMDBQueue来避免访问数据库竞争冲突

    FMDatabaseQueue解决这个问题的思路是:创建一个队列,然后将放入队列的block顺序执行,这样避免了多线程同时访问数据库

    然而FMDB还是不可避免的就蹦了

    报的就是databaseislocked

    然后就开始梳理多线程的冲突,尽量避免同一时间去初始化封装成的这个单例

    然而还是偶尔会崩溃

    然后还是从头开始在彻查了FMDB本身

    FMDatabase不能多线程使用同一个实例

    前面提过了,FMdatabase是无法多线程同时访问同一个FMDatabase实例的,否则会引起崩溃.

    官方给出的方案就是使用FMDatabaseQueue来解决这个问题,然而FMDatabaseQueue
    能够解决的问题仅仅在于多个数据库访问请求的冲突,并不能解决多线程中多次同时初始化FMDatabase引起的崩溃

    使用GCD解决多线程冲突

    在知道了这问题后,想到的解决方案就是:

    将不同线程多个初始化请求放入同一个队列排队依次执行

    具体步骤为:

    1.在单例初始化方法里初始化一个全局的串行队列

    - (instancetype)init

    {

    self = [super init];

    if (self) {

    NSString * dbPath = [PATH_USER_DOCUMENT stringByAppendingPathComponent:@"imcache.db"];

    dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath];

    [dbQueue inDatabase:^(FMDatabase *db) {

    [db executeUpdate:[IMCache createMessageTableSql]];

    [db executeUpdate:[IMCache createContectTableSql]];

    }];

    serialQueue =dispatch_queue_create("FMDBThread", DISPATCH_QUEUE_SERIAL);

    }

    return self;

    }

    2.在每一个访问DataBase的方法中调用dispatch_barrier_async()方法,使用此方法创建的任务首先会查看队列中有没有别的任务要执行,如果有,则会等待已有任务执行完毕再执行;同时在此方法后添加的任务必须等待此方法中任务执行后才能执行。这样,就能够顺利解决在inDatabase后还没有关闭就再次inDatabase。

    /**

    *  删除所有联系人的所有聊天记录

    */

    - (void)deleteAllMessages:(void(^)(BOOL success))completion

    {

    dispatch_barrier_async(serialQueue, ^(){

    [dbQueue inDatabase:^(FMDatabase *db) {

    // 查找出所有群的默认消息

    NSString * sql = [NSString stringWithFormat:@"SELECT * FROM messages WHERE savetime=%@", @"0"];

    NSMutableArray * messages = [NSMutableArray array];

    FMResultSet * result = [db executeQuery:sql];

    while ([result next]) {

    CacheMessage * message = [self valueToMessage:result];

    [messages addObject:message];

    }

    [result close];

    // 删除聊天表下所有记录

    NSString * deletesql = [NSString stringWithFormat:@"delete from messages"];

    BOOL delete = [db executeUpdate:deletesql];

    ZYNLog(@"删除聊天表 %d", delete);

    // 添加群的默认消息

    for (CacheMessage * msg in messages) {

    NSString * insert = [NSString stringWithFormat:@"insert into messages (fromcln,tocln,uid,type,contype,status,body,sendtime,receivetime,savetime,sessionid) values ('%@','%@','%@',%d,%d,%d,\"%@\",'%@','%@',%lld,'%@')", msg.from, msg.to, msg.uid, msg.type, msg.contype, msg.status, msg.body, msg.sendtime, msg.sendtime, msg.savetime, msg.sessionid];

    [db executeUpdate:insert];

    }

    ZYNLog(@"添加群默认消息完成");

    completion(true);

    }];

    });

    }

    相关文章

      网友评论

      • iii余光:FMDatabase 写成单例不就不会多次初始化了 么 然后queue进行并发线程安全操作

      本文标题:关于FMDB报databaseislocked的解决方案

      本文链接:https://www.haomeiwen.com/subject/dypmsttx.html