關于CoreData和SQLite多線程訪問時的線程安全問題
數(shù)據(jù)庫讀取操作一般都是多線程訪問的。在對數(shù)據(jù)進行讀取時,我們要保證其當前狀態(tài)不能被修改,即讀取時加鎖,否則就會出現(xiàn)數(shù)據(jù)錯誤混亂。
IOS中常用的兩種數(shù)據(jù)持久化存儲方式:CoreData和SQLite,兩者都需要設置線程安全,在這里以FMDB來解釋對SQLite的線程安全訪問。
一:FMDB的線程安全:(以讀取圖片為例)
1.沒有線程安全的執(zhí)行方式:
//************** 數(shù)據(jù)庫保存圖片 ******************//
FMDatabase *database = [FMDatabase databaseWithPath:[self getDatabasePath]];
//打開數(shù)據(jù)庫
[database open];
NSString *sql = @"create table if not exists Test (id integer primary key autoincrement,name text,image blob);";
//創(chuàng)建表
[database executeUpdate:sql];
//把UIImage對象轉化為NSData
NSData *data = UIImagePNGRepresentation([UIImage imageNamed:@"user_browse"]);
//寫入數(shù)據(jù)
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,使用線程隊列
//************** 數(shù)據(jù)庫線程安全 ***********//
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);";
//創(chuàng)建表
[database executeUpdate:sql];
}];
//插入數(shù)據(jù)
[queue inDatabase:^(FMDatabase *db) {
//寫入數(shù)據(jù)
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的實現(xiàn):
在當使用FMDBDatabaseQueue創(chuàng)建數(shù)據(jù)庫時,會使用GCD創(chuàng)建一個線程隊列:
。。。
_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中會鎖定當前數(shù)據(jù)庫
dispatch_sync(_queue, ^() {
FMDatabase *db = [self database];
block(db);
……
}
我們可以看到實際上這里是對整個數(shù)據(jù)庫進行加鎖,以此來保證線程安全的。
二、CoreData的線程安全
1.沒有線程安全的coredata數(shù)據(jù)讀?。?/p>
NSManagedObjectContext對象的創(chuàng)建:_managedObjectContext = [[NSManagedObjectContext alloc] init];
插入數(shù)據(jù)操作:(AppDetailModal為數(shù)據(jù)模型)
context 為返回的 _managedObjectContext
AppDetailModal *newapp = [NSEntityDescription insertNewObjectForEntityForName:TableName inManagedObjectContext:context];
其他查詢、更新、刪除操作
//獲取Entity
NSEntityDescription *entity = [NSEntityDescription entityForName:TableName inManagedObjectContext:context];
2.線程安全的coreData操作:
首先創(chuàng)建并行的NSManagedObjectContext對象
NSManagedObjectContext* context=[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
然后在執(zhí)行讀取操作時使用一下兩個方法:
-(void)performBlock:(void (^)(void))block
-(void)performBlockAndWait:(void (^)(void))block
[context performBlock:^{
//要執(zhí)行的讀取操作
}];
Written with StackEdit.