關于CoreData和SQLite多線程訪問時的線程安全問題

關于CoreData和SQLite多線程訪問時的線程安全問題

數據庫讀取操作一般都是多線程訪問的。在對數據進行讀取時,我們要保證其當前狀態不能被修改,即讀取時加鎖,否則就會出現數據錯誤混亂。
IOS中常用的兩種數據持久化存儲方式:CoreData和SQLite,兩者都需要設置線程安全,在這里以FMDB來解釋對SQLite的線程安全訪問。

一:FMDB的線程安全:(以讀取圖片為例)

1.沒有線程安全的執行方式:

   //************** 數據庫保存圖片  ******************//

    FMDatabase *database = [FMDatabase databaseWithPath:[self getDatabasePath]];

    //打開數據庫

    [database open];

    NSString *sql = @"create table if not exists Test (id integer primary key autoincrement,name text,image blob);";

    //創建表

    [database executeUpdate:sql];

    //把UIImage對象轉化為NSData

    NSData *data = UIImagePNGRepresentation([UIImage imageNamed:@"user_browse"]);    

    //寫入數據

    sql = @"insert into Test (name,image) values (?,?)";

    [database executeUpdate:sql,@"張三",data];

    //讀取顯示

    sql = @"select * from Test;";

    FMResultSet *resultSet = [database executeQuery:sql];

    while (resultSet.next)

    {

        //[resultSet dataForColumn:@"image"];

        NSData *imageData = [resultSet dataForColumnIndex:2];

        UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];

        imageView.image = [UIImage imageWithData:imageData];

        [self.view addSubview:imageView];

    }

2,使用線程隊列

//************** 數據庫線程安全 ***********//
    FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithPath:[self getDatabasePath]];

    [queue inDatabase:^(FMDatabase *db) {

        //線程安全的

        __block NSString *sql = @"create table if not exists Test (id integer primary key autoincrement,name text,image blob);";

        //創建表

        [database executeUpdate:sql];

    }];

    //插入數據

    [queue inDatabase:^(FMDatabase *db) {

        //寫入數據

        sql = @"insert into Test (name,image) values (?,?)";

        [database executeUpdate:sql,@"張三",data];

    }];

    //讀取
    [queue inDatabase:^(FMDatabase *db) {
        //讀取顯示
        sql = @"select * from Test;";
        FMResultSet *resultSet = [database executeQuery:sql];
        while (resultSet.next)
        {
            //[resultSet dataForColumn:@"image"];

            NSData *imageData = [resultSet dataForColumnIndex:2];
            UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];

            imageView.image = [UIImage imageWithData:imageData];
            [self.view addSubview:imageView];
        }
    }];

分析一下線程安全下的FMDB的實現:
在當使用FMDBDatabaseQueue創建數據庫時,會使用GCD創建一個線程隊列:

。。。
 _queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
        dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL);
        _openFlags = openFlags;
。。。

然后在讀取時調用[queue inDatabase:^(FMDatabase *db)方法,在block中會鎖定當前數據庫

dispatch_sync(_queue, ^() {
        FMDatabase *db = [self database];
        block(db);
    ……
}

我們可以看到實際上這里是對整個數據庫進行加鎖,以此來保證線程安全的。

二、CoreData的線程安全

1.沒有線程安全的coredata數據讀?。?/p>

NSManagedObjectContext對象的創建:_managedObjectContext = [[NSManagedObjectContext alloc] init];

插入數據操作:(AppDetailModal為數據模型)

context 為返回的 _managedObjectContext

AppDetailModal *newapp = [NSEntityDescription insertNewObjectForEntityForName:TableName inManagedObjectContext:context];

其他查詢、更新、刪除操作
//獲取Entity

    NSEntityDescription *entity = [NSEntityDescription entityForName:TableName inManagedObjectContext:context];

2.線程安全的coreData操作:

首先創建并行的NSManagedObjectContext對象

NSManagedObjectContext* context=[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 

然后在執行讀取操作時使用一下兩個方法:

  • -(void)performBlock:(void (^)(void))block

  • -(void)performBlockAndWait:(void (^)(void))block

[context performBlock:^{

        //要執行的讀取操作

 }];

Written with StackEdit.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容