FMDB 源码阅读
2018-11-09 本文已影响0人
取个名字真困难
面试的时候被问到了 说FMDB如何是实现数据的安全的,我当时没有去看源码,直接猜测是加了锁,后来发现不是,现在补上功课。跟我有一样想法的小伙伴一起分享。
1.主要涉及的类
FMDdatabase,FMResultSet,FMDatabaseQueue,FMDatabaseAddition,FMDatabasePool
2.源码分析:
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
点进去发现调用的是
+ (instancetype)databaseQueueWithPath:(NSString*)aPath {
FMDatabaseQueue*q = [[self alloc]initWithPath:aPath];
FMDBAutorelease(q);
return q;
}
继续点进去看[[self alloc]initWithPath:aPath];
- (instancetype)initWithPath:(NSString*)aPath {
// default flags for sqlite3_open
return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:nil];
}
继续走看 [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:nil];
- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString*)vfsName {
self= [superinit];
if(self!=nil) {
_db = [[[self class] databaseClass] databaseWithPath:aPath];
FMDBRetain(_db);
#if SQLITE_VERSION_NUMBER >=3005000
BOOL success = [_dbopenWithFlags:openFlagsvfs:vfsName];
#else
BOOLsuccess = [_db open];
#endif
if(!success) {
NSLog(@"Could not create database queue for path %@", aPath);
FMDBRelease(self);
return0x00;
}
_path=FMDBReturnRetained(aPath);
_queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); //这里创建了一个自定义串行队列
dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL); //添加标记 根据标记取出来,确保是同一个队列
_openFlags= openFlags;
_vfsName= [vfsNamecopy];
}
return self;
}
具体更新数据的API
[queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
FMResultSet *rs = [db executeQuery:@"select * from foo"];
while ([rs next]) {
//…
}
}];
点进去inDatabase 会来到下面的这个方法
- (void)inDatabase:(__attribute__((noescape))void(^)(FMDatabase*db))block {
#ifndef NDEBUG
/* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue
* and then check it against self to make sure we're not about to deadlock. */
FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey);//根据kDispatchQueueSpecificKey取出来FMDatabaseQueue(当初存进去的是self)
assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock");
通过判断currentSyncQueue和 self 达到判断 FMDatabaseQueue 中的queue的目的。每一个FMDatabaseQueue对象都有一个dispatch_Queue_t. 大神的代码果然有思想。
断言确保当前要执行的队列和正在执行的队列不是同一个队列,否则会引起死锁。
#endif
FMDBRetain(self);
下面是在串行队列发起一个同步任务。
dispatch_sync(_queue, ^() {
FMDatabase*db = [self database];
block(db);
if([dbhasOpenResultSets]) {
NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]");
#if defined(DEBUG) && DEBUG
NSSet*openSetCopy =FMDBReturnAutoreleased([[dbvalueForKey:@"_openResultSets"]copy]);
for(NSValue*rsInWrappedInATastyValueMeal in openSetCopy) {
FMResultSet*rs = (FMResultSet*)[rsInWrappedInATastyValueMealpo interValue];
NSLog(@"query: '%@'", [rs query]);
}
#endif
}
});
FMDBRelease(self);
}
3.总结
FMDB 主要使用串行队列执行同步任务的方式来保证数据的安全的。