FMDB 源码阅读

面试的时候被问到了 说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 主要使用串行队列执行同步任务的方式来保证数据的安全的。

你可能感兴趣的:(FMDB 源码阅读)