FMDB database locked問題解析

FMDB可能是iOS中最常用的數(shù)據(jù)庫第三方框架. 在項目中, 對于簡單的數(shù)據(jù), 我們一般是儲存在偏好設(shè)置中, 或者存入XML文件, 需要的時候再讀取. 但是對于大量的數(shù)據(jù), 或者是需要后續(xù)頻繁操作的數(shù)據(jù), 我們只能把這些數(shù)據(jù)存入數(shù)據(jù)庫中.

在最近的一個項目中, 就遇到了需要頻繁操作數(shù)據(jù)庫內(nèi)數(shù)據(jù)的情況, 頻繁的增刪讀寫.隨之而來的就遇到了數(shù)據(jù)庫鎖死問題. FMDB報錯為database locked.

首先先弄懂為什么會出現(xiàn)數(shù)據(jù)庫鎖死問題. 當(dāng)A在寫入數(shù)據(jù)的時候, 會先鎖定數(shù)據(jù)庫再進行寫入操作. 然而這時, B又馬上想寫入數(shù)據(jù), 因為數(shù)據(jù)庫已經(jīng)被A鎖住了, 所以B無法進行寫入操作, 只能先等待A操作完成后再打開數(shù)據(jù)庫. 當(dāng)?shù)却臅r間過長的時候, 數(shù)據(jù)庫就會報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對象. 那我們應(yīng)該怎么處理數(shù)據(jù)庫鎖死問題呢? 官方已經(jīng)給出了答案:

Instead, use FMDatabaseQueue.

然后, 樓主參考了網(wǎng)絡(luò)各路大神的意見, 一致的解決版本是建立一個DatabaseHelper的類, 用來管理數(shù)據(jù)庫, 應(yīng)該說是管理數(shù)據(jù)庫線程. 即是把FMDatabaseQueue做成一個單例, 所有的的數(shù)據(jù)庫操作在這個線程中串行執(zhí)行. 當(dāng)需要執(zhí)行一個操作時, 先把操作放入串行隊列, 執(zhí)行完前一個操作再執(zhí)行下一個操作.

上代碼:

#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) {
        //創(chuàng)建表
        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

之后, 只需要把對數(shù)據(jù)庫的各種操作都放入- (void)inDatabase:(void(^)(FMDatabase*))block內(nèi)執(zhí)行就行了.


由于筆者知識有限,如有錯誤,歡迎討論指出。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容