FMDB database locked问题解析

作者: KenZhangCn | 来源:发表于2016-12-30 10:50 被阅读185次

    FMDB可能是iOS中最常用的数据库第三方框架. 在项目中, 对于简单的数据, 我们一般是储存在偏好设置中, 或者存入XML文件, 需要的时候再读取. 但是对于大量的数据, 或者是需要后续频繁操作的数据, 我们只能把这些数据存入数据库中.

    在最近的一个项目中, 就遇到了需要频繁操作数据库内数据的情况, 频繁的增删读写.随之而来的就遇到了数据库锁死问题. FMDB报错为database locked.

    首先先弄懂为什么会出现数据库锁死问题. 当A在写入数据的时候, 会先锁定数据库再进行写入操作. 然而这时, B又马上想写入数据, 因为数据库已经被A锁住了, 所以B无法进行写入操作, 只能先等待A操作完成后再打开数据库. 当等待的时间过长的时候, 数据库就会报database locked错误. 这个时间好像是2s.

    这里先贴出源码中的一句话 :

    Using a single instance of <FMDatabase> from multiple threads at once is a bad idea. It has always been OK to make a <FMDatabase> object per thread. Just don't share a single instance across threads, and definitely not across multiple threads at the same time.

    告诉我们别妄想着把database做成单例, 也别想着使用多线程访问database对象. 那我们应该怎么处理数据库锁死问题呢? 官方已经给出了答案:

    Instead, use FMDatabaseQueue.

    然后, 楼主参考了网络各路大神的意见, 一致的解决版本是建立一个DatabaseHelper的类, 用来管理数据库, 应该说是管理数据库线程. 即是把FMDatabaseQueue做成一个单例, 所有的的数据库操作在这个线程中串行执行. 当需要执行一个操作时, 先把操作放入串行队列, 执行完前一个操作再执行下一个操作.

    上代码:

    #import "BPBDBHelper.h"
    
    @implementation BPBDBHelper
    
    {
        FMDatabaseQueue* queue;
    }
    
    - (id)init {
        self = [super init];
        if(self){
            NSString *dbFilePath = [self getDatabasePath];
            queue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath];
        }
        return self;
    }
    
    + (BPBDBHelper *)sharedInstance {
        static dispatch_once_t pred = 0;
        __strong static id _sharedObject = nil;
        dispatch_once(&pred, ^{
            _sharedObject = [[self alloc] init];
        });
        return _sharedObject;
    }
    
    - (void)inDatabase:(void(^)(FMDatabase*))block {
        [queue inDatabase:^(FMDatabase *db){
            if ([db open]) {
                block(db);
            }
            [db close];
        }];
    }
    
    - (BOOL)creatDatabase {
        [queue inDatabase:^(FMDatabase *db) {
            //创建表
            if ([db open]) {
                NSString *positionSql = @"CREATE TABLE IF NOT EXISTS 'position' (******)";
                [db executeUpdate:positionSql];
           }
            [db close];
        }];
        return true;
    }
    
    - (NSString *)getDatabasePath {
        NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
        NSString *filePath = [documentsPath stringByAppendingPathComponent:@"position.sqlite"];
        return filePath;
    }
    
    + (void)refreshDatabaseFile {
        BPBDBHelper *instance = [self sharedInstance];
        [instance doRefresh];
    }
    
    - (void)doRefresh {
        NSString *dbFilePath = [self getDatabasePath];
        queue = [FMDatabaseQueue databaseQueueWithPath:dbFilePath];
    }
    @end
    

    之后, 只需要把对数据库的各种操作都放入- (void)inDatabase:(void(^)(FMDatabase*))block内执行就行了.


    由于笔者知识有限,如有错误,欢迎讨论指出。

    相关文章

      网友评论

        本文标题:FMDB database locked问题解析

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