關于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數據讀取:
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.