Database read operations are typically accessed in multiple threads. When reading the data, we want to ensure that its current state can not be modified, that is, read the lock, otherwise there will be data error confusion.
There are two commonly used data persistence methods in iOS: CoreData and SQLite, both of which need to be thread-safe to explain thread-safe access to SQLite in Fmdb.
One: Fmdb thread safety: (take picture as an example)
1. There is no thread-safe way to perform:
123456789101112131415161718192021222324252627282930313233343536373839404142434445 |
//************** 数据库保存图片 ******************//
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, using thread queue
12345678910111213141516171819202122232425262728293031323334353637383940414243 |
//************** 数据库线程安全 ***********//
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];
}
}];
|
Analyze the implementation of Fmdb under thread safety:
When you create a database using Fmdbdatabasequeue, a thread queue is created with GCD:
12345 |
。。。
_queue = dispatch_queue_create([[
NSString
stringWithFormat:@
"fmdb.%@"
,
self
] UTF8String],
NULL
);
dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge
void
*)
self
,
NULL
);
_openFlags = openFlags;
。。。
|
The method is then called at read Time [queue inDatabase:^(FMDatabase *db)
, and the current database is locked in the block
12345 |
dispatch_sync(_queue, ^() { FMDatabase *db = [ self database]; block(db); …… } |
We can see that this is actually a lock-up of the entire database to ensure thread safety.
Second, CoreData thread safety
1. No thread-safe coredata data reads:
The creation of the Nsmanagedobjectcontext object:_managedObjectContext = [[NSManagedObjectContext alloc] init];
Insert Data operation: (Appdetailmodal as data model)
Context for the returned _managedobjectcontext
1 |
AppDetailModal *newapp = [ NSEntityDescription insertNewObjectForEntityForName:TableName inManagedObjectContext:context]; |
Other query, update, delete operations
Get entity
1 |
NSEntityDescription *entity = [ NSEntityDescription entityForName:TableName inManagedObjectContext:context]; |
2. Thread-Safe CoreData operation:
Create a parallel Nsmanagedobjectcontext object first
1 |
NSManagedObjectContext * context=[[ NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType ]; |
12345 |
然后在执行读取操作时使用一下两个方法: -( void )performBlock:( void (^)( void ))block -( void )performBlockAndWait:( void (^)( void ))block |
12345 |
[context performBlock:^{ //要执行的读取操作 }]; |
Thread safety issues with CoreData and SQLite multi-threaded access