all is well

小说:兼职赚钱的方法作者:开华更新时间:2018-09-20字数:92998

这时候,周围的那些鬼子军舰上的小鬼子听闻了枪炮声,便纷纷冲出来,朝这边张望着,那个鬼子头头松岛正一左一右搂着两个卖淫女子淫欢作乐呢,听得枪炮声响起,便急忙推开女子,连衣服都没穿上,慌张跑出来,扯着嗓子问手下:“发生什么事情了,枪声怎么回事?”

互联网医疗

唐三巴不得他们向自己发动攻击还要好一些,这种不断的骚扰反而令两人面对起来更加困难。试探着将蓝色领域收回一些,来吸引血蝙蝠。
悟空立时便知道,这是如来送来的大礼啊,如来和覆海蛟几无交集,他为何要将黄角大仙交给覆海蛟呢?悟空问道:“这是……黄角大仙?”

他确实有些奇怪,自己没有联系到盗一,也没有和他说过啊,他是怎么知道自己在找人呢。

上一篇博客讲述SQLite的使用,本篇将讲述FMDB源码,后面也会讲解SQLite在使用与FMDB的区别。本篇读下来大约20-30分钟,建议大家先收藏一下。

 

FMDB是以OC方式封装SQLite中C语言的API,也是iOS中SQLite数据库的框架,在目前研发项目中使用的也是比较广泛的。下面直入正题

 

一、FMDB源码结构

首先我们来看一下FMDB的源码的结构与组成,如下图:

我们可以从结构上看出FMDB在共有5个文件组成,其中FMDB.h用于管理其他5个文件,下面分别讲述5个文件的用处

(1)FMDatabase:代表一个单独的SQLite操作实例,数据库通过它增删改查操作;

(2)FMResultSet:代表查询后的结果集;

(3)FMDatabaseQueue:代表串行队列,对多线程操作提供了支持;

(4)FMDatabaseAdditions:本类用于扩展FMDatabase,用于查找表是否存在,版本号等功能;

(5)FMDatabasePool:此方式官方是不推荐使用,代表是任务池,也是对多线程提供了支持。

下面将具体讲述每一个类的核心代码是怎么样的?

 

二、FMDB源码解析

2.1 FMDatabase源码解析

2.1.1 打开数据库连接

- (BOOL)open;是对SQLite中sqlite3_open()函数的封装使用

下面看一下具体使用

- (BOOL)open {
    if (_isOpen) {
        return YES;
    }
    if (_db) {
        [self close];
    }
    // now open database
    int err = sqlite3_open([self sqlitePath], (sqlite3**)&_db );
    if(err != SQLITE_OK) {
        NSLog(@"error opening!: %d", err);
        return NO;
    }
    //当执行这段代码的时候,数据库正在被其他线程访问,就需要给他设置重试时间,
    if (_maxBusyRetryTimeInterval > 0.0) {
        // set the handler
        
        [self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval];
    }
    
    _isOpen = YES;
    
    return YES;
}

//下面我们看一下setMaxBusyRetryTimeInterval的实现方法
- (void)setMaxBusyRetryTimeInterval:(NSTimeInterval)timeout {
    
    _maxBusyRetryTimeInterval = timeout;
    
    if (!_db) {
        return;
    }
    
    if (timeout > 0) {
        sqlite3_busy_handler(_db, &FMDBDatabaseBusyHandler, (__bridge void *)(self));
    }
    else {
        // turn it off otherwise
        sqlite3_busy_handler(_db, nil, nil);
    }
}

上面是打开数据库连接,上面红色字体标注的方法setMaxBusyRetryTimeInterval()设置重试时间,相当于SQLite中调用int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);

 针对int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);该函数

(1)第一个参数:哪个数据库需要设置busy_handler

(2)第二个参数:需要回调的busy handler,调用次函数时,需要传参,是sqlite3_busy_handler第三个参数

(3)第三个参数:int参数代表锁事件,该函数被调用次数,如果返回为0,不会再次访问数据库,返回非0,将不断尝试访问数据库。

当获取不到锁时,会执行回调函数的次数以此来延时,等待其他线程等操作完数据库,这样获得操作数据库。

 

2.1.2 查询数据库

executeQuery函数是数据库比较重要的方法。

在看实现文件

- (FMResultSet *)executeQuery:(NSString*)sql, ... {
    va_list args;
    va_start(args, sql);
    //整个方法关键是下面一句
    id result = [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args];
    
    va_end(args);
    return result;
}

从上面可以看出:调用executeQuery函数,实际上是调用

- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args函数,下面看下此函数的实现方式。

- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
    //判断数据库是否存在
    if (![self databaseExists]) {
        return 0x00;
    }
    //判断数据库是否已经在使用当中
    if (_isExecutingStatement) {
        [self warnInUse];
        return 0x00;
    }
    
    _isExecutingStatement = YES;
    
    int rc                  = 0x00;
    sqlite3_stmt *pStmt     = 0x00;
    FMStatement *statement  = 0x00;
    FMResultSet *rs         = 0x00;
    //打印sql语句
    if (_traceExecution && sql) {
        NSLog(@"%@ executeQuery: %@", self, sql);
    }
    
    //获取缓存数据
    if (_shouldCacheStatements) {
        statement = [self cachedStatementForQuery:sql];
        pStmt = statement ? [statement statement] : 0x00;
        [statement reset];
    }
    
    //没有缓存数据,直接查询数据库
    if (!pStmt) {
        //对sql语句进行预处理,生成预处理过的“sql语句”pStmt。
        rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
        
        if (SQLITE_OK != rc) {//出错处理
            if (_logsErrors) {
                NSLog(@"DB Error: %d "%@"", [self lastErrorCode], [self lastErrorMessage]);
                NSLog(@"DB Query: %@", sql);
                NSLog(@"DB Path: %@", _databasePath);
            }
            
            if (_crashOnErrors) {
                NSAssert(false, @"DB Error: %d "%@"", [self lastErrorCode], [self lastErrorMessage]);
                abort();
            }
            
            sqlite3_finalize(pStmt);
            _isExecutingStatement = NO;
            return nil;
        }
    }
    
    id obj;
    int idx = 0;
    int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)
    
    // If dictionaryArgs is passed in, that means we are using sqlite"s named parameter support
    //对dictionaryArgs参数的处理,类似于下面":age"参数形式
    if (dictionaryArgs) {
        
        for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
            
            // Prefix the key with a colon.
            NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
            
            if (_traceExecution) {
                NSLog(@"%@ = %@", parameterName, [dictionaryArgs objectForKey:dictionaryKey]);
            }
            
            // Get the index for the parameter name.
            int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
            
            FMDBRelease(parameterName);
            
            if (namedIdx > 0) {
                // Standard binding from here.
                [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
                // increment the binding count, so our check below works out
                idx++;
            }
            else {
                NSLog(@"Could not find index for %@", dictionaryKey);
            }
        }
    }
    else {//对于arrayArgs参数和不定参数的处理,类似于"?"参数形式
        
        while (idx < queryCount) {
            
            if (arrayArgs && idx < (int)[arrayArgs count]) {
                obj = [arrayArgs objectAtIndex:(NSUInteger)idx];
            }
            else if (args) {//不定参数形式
                obj = va_arg(args, id);
            }
            else {
                //We ran out of arguments
                break;
            }
            
            if (_traceExecution) {
                if ([obj isKindOfClass:[NSData class]]) {
                    NSLog(@"data: %ld bytes", (unsigned long)[(NSData*)obj length]);
                }
                else {
                    NSLog(@"obj: %@", obj);
                }
            }
            
            idx++;
            
            [self bindObject:obj toColumn:idx inStatement:pStmt];
        }
    }
    
    if (idx != queryCount) {//如果绑定的参数数目不对,则进行出错处理
        NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
        sqlite3_finalize(pStmt);
        _isExecutingStatement = NO;
        return nil;
    }
    
    FMDBRetain(statement); // to balance the release below
    
    if (!statement) {//生成FMStatement对象
        statement = [[FMStatement alloc] init];
        [statement setStatement:pStmt];
        //缓存的处理,key为sql语句,值为statement
        if (_shouldCacheStatements && sql) {
            [self setCachedStatement:statement forQuery:sql];
        }
    }
    
    // the statement gets closed in rs"s dealloc or [rs close];
    rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self];
    [rs setQuery:sql];
    
    NSValue *openResultSet = [NSValue valueWithNonretainedObject:rs];
    [_openResultSets addObject:openResultSet];
    
    [statement setUseCount:[statement useCount] + 1];
    
    FMDBRelease(statement);
    
    _isExecutingStatement = NO;
    
    return rs;
}

发现上面那个函数有四个参数,看到源码之后,我们一一讲述四个参数:

(1)第一个参数sql:代表我们要查询的sql语句;

(2)第二个参数arrayArgs:代表数组类型的参数,举例如下:

FMResultSet *resultSet = [_db executeQuery:@"SELECT * FROM t_student WHERE age > ?" withArgumentsInArray:@[@25]];

(3)第三个参数dictionaryArgs:代表字典类型的参数,举例如下:

    FMResultSet *resultSet = [_db executeQuery:@"SELECT * FROM t_student WHERE age > :age" withParameterDictionary:@{@"age":@25}];

(4)第四个参数args:代表可变类型的参数,举例如下:

FMResultSet *resultSet = [_db executeQuery:@"SELECT * FROM t_student WHERE age > ?",@(20)];

上面是查询基本源码讲解,大家可以详细里面的源码实现,里面也有讲解。

 

2.1.3 更新数据库

针对FMDB数据库增删改都属于对数据库的更新操作,FMDB通过executeUpdate系列函数来实现对数据库的更新操作。

- (BOOL)executeUpdate:(NSString*)sql, ...;系列函数,我们来看一下executeUpdate函数的实现。

- (BOOL)executeUpdate:(NSString*)sql, ... {
    va_list args;
    va_start(args, sql);
    //主要是下面这个函数
    BOOL result = [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:nil orVAList:args];
    
    va_end(args);
    return result;
}

我们再来看一下红色标出的代码实现,- (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args函数实现。

#pragma mark Execute updates

- (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
    
    if (![self databaseExists]) {
        return NO;
    }
    
    if (_isExecutingStatement) {
        [self warnInUse];
        return NO;
    }
    
    _isExecutingStatement = YES;
    
    int rc                   = 0x00;
    sqlite3_stmt *pStmt      = 0x00;
    FMStatement *cachedStmt  = 0x00;
    
    if (_traceExecution && sql) {
        NSLog(@"%@ executeUpdate: %@", self, sql);
    }
    
    if (_shouldCacheStatements) {
        cachedStmt = [self cachedStatementForQuery:sql];
        pStmt = cachedStmt ? [cachedStmt statement] : 0x00;
        [cachedStmt reset];
    }
    
    if (!pStmt) {
        rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
        
        if (SQLITE_OK != rc) {
            if (_logsErrors) {
                NSLog(@"DB Error: %d "%@"", [self lastErrorCode], [self lastErrorMessage]);
                NSLog(@"DB Query: %@", sql);
                NSLog(@"DB Path: %@", _databasePath);
            }
            
            if (_crashOnErrors) {
                NSAssert(false, @"DB Error: %d "%@"", [self lastErrorCode], [self lastErrorMessage]);
                abort();
            }
            
            if (outErr) {
                *outErr = [self errorWithMessage:[NSString stringWithUTF8String:sqlite3_errmsg(_db)]];
            }
            
            sqlite3_finalize(pStmt);
            
            _isExecutingStatement = NO;
            return NO;
        }
    }
    
    id obj;
    int idx = 0;
    int queryCount = sqlite3_bind_parameter_count(pStmt);
    
    // If dictionaryArgs is passed in, that means we are using sqlite"s named parameter support
    if (dictionaryArgs) {
        
        for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
            
            // Prefix the key with a colon.
            NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
            
            if (_traceExecution) {
                NSLog(@"%@ = %@", parameterName, [dictionaryArgs objectForKey:dictionaryKey]);
            }
            // Get the index for the parameter name.
            int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
            
            FMDBRelease(parameterName);
            
            if (namedIdx > 0) {
                // Standard binding from here.
                [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
                
                // increment the binding count, so our check below works out
                idx++;
            }
            else {
                NSString *message = [NSString stringWithFormat:@"Could not find index for %@", dictionaryKey];
                
                if (_logsErrors) {
                    NSLog(@"%@", message);
                }
                if (outErr) {
                    *outErr = [self errorWithMessage:message];
                }
            }
        }
    }
    else {
        
        while (idx < queryCount) {
            
            if (arrayArgs && idx < (int)[arrayArgs count]) {
                obj = [arrayArgs objectAtIndex:(NSUInteger)idx];
            }
            else if (args) {
                obj = va_arg(args, id);
            }
            else {
                //We ran out of arguments
                break;
            }
            
            if (_traceExecution) {
                if ([obj isKindOfClass:[NSData class]]) {
                    NSLog(@"data: %ld bytes", (unsigned long)[(NSData*)obj length]);
                }
                else {
                    NSLog(@"obj: %@", obj);
                }
            }
            
            idx++;
            
            [self bindObject:obj toColumn:idx inStatement:pStmt];
        }
    }
    
    
    if (idx != queryCount) {
        NSString *message = [NSString stringWithFormat:@"Error: the bind count (%d) is not correct for the # of variables in the query (%d) (%@) (executeUpdate)", idx, queryCount, sql];
        if (_logsErrors) {
            NSLog(@"%@", message);
        }
        if (outErr) {
            *outErr = [self errorWithMessage:message];
        }
        
        sqlite3_finalize(pStmt);
        _isExecutingStatement = NO;
        return NO;
    }
    
    /* Call sqlite3_step() to run the virtual machine. Since the SQL being
     ** executed is not a SELECT statement, we assume no data will be returned.
     */
    
    rc      = sqlite3_step(pStmt);
    
    if (SQLITE_DONE == rc) {
        // all is well, let"s return.
    }
    else if (SQLITE_INTERRUPT == rc) {
        if (_logsErrors) {
            NSLog(@"Error calling sqlite3_step. Query was interrupted (%d: %s) SQLITE_INTERRUPT", rc, sqlite3_errmsg(_db));
            NSLog(@"DB Query: %@", sql);
        }
    }
    else if (rc == SQLITE_ROW) {
        NSString *message = [NSString stringWithFormat:@"A executeUpdate is being called with a query string "%@"", sql];
        if (_logsErrors) {
            NSLog(@"%@", message);
            NSLog(@"DB Query: %@", sql);
        }
        if (outErr) {
            *outErr = [self errorWithMessage:message];
        }
    }
    else {
        if (outErr) {
            *outErr = [self errorWithMessage:[NSString stringWithUTF8String:sqlite3_errmsg(_db)]];
        }
        
        if (SQLITE_ERROR == rc) {
            if (_logsErrors) {
                NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db));
                NSLog(@"DB Query: %@", sql);
            }
        }
        else if (SQLITE_MISUSE == rc) {
            // uh oh.
            if (_logsErrors) {
                NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db));
                NSLog(@"DB Query: %@", sql);
            }
        }
        else {
            // wtf?
            if (_logsErrors) {
                NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db));
                NSLog(@"DB Query: %@", sql);
            }
        }
    }
    
    if (_shouldCacheStatements && !cachedStmt) {
        cachedStmt = [[FMStatement alloc] init];
        
        [cachedStmt setStatement:pStmt];
        
        [self setCachedStatement:cachedStmt forQuery:sql];
        
        FMDBRelease(cachedStmt);
    }
    
    int closeErrorCode;
    
    if (cachedStmt) {
        [cachedStmt setUseCount:[cachedStmt useCount] + 1];
        closeErrorCode = sqlite3_reset(pStmt);
    }
    else {
        /* Finalize the virtual machine. This releases all memory and other
         ** resources allocated by the sqlite3_prepare() call above.
         */
        closeErrorCode = sqlite3_finalize(pStmt);
    }
    
    if (closeErrorCode != SQLITE_OK) {
        if (_logsErrors) {
            NSLog(@"Unknown error finalizing or resetting statement (%d: %s)", closeErrorCode, sqlite3_errmsg(_db));
            NSLog(@"DB Query: %@", sql);
        }
    }
    
    _isExecutingStatement = NO;
    return (rc == SQLITE_DONE || rc == SQLITE_OK);
}

我们看完了发现它和executeQuery函数有很多相似的地方,源码标注大家可以看一下executeQuery的标注,也是有几个参数,参数的形式也差不多,就是多了一个error,错误的输出语句。

 

2.1.4 执行多条sql

一次性来执行多条的sql语句对于数据库来说也是常用的操作。FMDB通过使用executeStatements函数来执行多条sql语句

- (BOOL)executeStatements:(NSString *)sql;系列函数来操作,下面看一下函数实现方式

- (BOOL)executeStatements:(NSString *)sql {
    return [self executeStatements:sql withResultBlock:nil];
}

上面通过调用executeStatements函数调用,我们再进一步看executeStatements的实现方式。

- (BOOL)executeStatements:(NSString *)sql withResultBlock:(__attribute__((noescape)) FMDBExecuteStatementsCallbackBlock)block {
    
    int rc;
    char *errmsg = nil;
    
    rc = sqlite3_exec([self sqliteHandle], [sql UTF8String], block ? FMDBExecuteBulkSQLCallback : nil, (__bridge void *)(block), &errmsg);
    
    if (errmsg && [self logsErrors]) {
        NSLog(@"Error inserting batch: %s", errmsg);
        sqlite3_free(errmsg);
    }
    
    return (rc == SQLITE_OK);
}

上面函数,发现有SQLite,其实对sqlite3_exec函数的封装,完成对多条sql语句的查找。这样讲可能不是很清晰,举例一下:

   //多个SQL执行语句入一个字符串中执行
 - (void)executeStatementsTest{
    NSString *sql =
    @"CREATE TABLE IF NOT EXISTS t_student_tmp (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL);"
    "INSERT INTO t_student_tmp (name, age) VALUES ("yixiang", 10);"
    "INSERT INTO t_student_tmp (name, age) VALUES ("yixiangXX", 20);";
    BOOL success = [_db executeStatements:sql];
    if (success) {
        NSLog(@"执行成功");
    }else{
        NSLog(@"执行失败");
    }
    
    sql = @"SELECT * FROM t_student;"
    "SELECT * FROM t_student_tmp;";
    success = [_db executeStatements:sql withResultBlock:^int(NSDictionary *resultsDictionary) {
        NSLog(@"%@",resultsDictionary);//查询结果都在resultsDictionary
        return 0;
    }];
    if (success) {
        NSLog(@"查询成功");
    }else{
        NSLog(@"查询失败");
    }
    
}

 

2.1.6 加解密

在FMDatabase还有一个功能,就是对FMDB进行加解密处理,下面为实现方式

#pragma mark Key routines

- (BOOL)rekey:(NSString*)key {
    NSData *keyData = [NSData dataWithBytes:(void *)[key UTF8String] length:(NSUInteger)strlen([key UTF8String])];
    
    return [self rekeyWithData:keyData];
}

- (BOOL)rekeyWithData:(NSData *)keyData {
#ifdef SQLITE_HAS_CODEC
    if (!keyData) {
        return NO;
    }
    
    int rc = sqlite3_rekey(_db, [keyData bytes], (int)[keyData length]);
    
    if (rc != SQLITE_OK) {
        NSLog(@"error on rekey: %d", rc);
        NSLog(@"%@", [self lastErrorMessage]);
    }
    
    return (rc == SQLITE_OK);
#else
#pragma unused(keyData)
    return NO;
#endif
}

- (BOOL)setKey:(NSString*)key {
    NSData *keyData = [NSData dataWithBytes:[key UTF8String] length:(NSUInteger)strlen([key UTF8String])];
    
    return [self setKeyWithData:keyData];
}

- (BOOL)setKeyWithData:(NSData *)keyData {
#ifdef SQLITE_HAS_CODEC
    if (!keyData) {
        return NO;
    }
    
    int rc = sqlite3_key(_db, [keyData bytes], (int)[keyData length]);
    
    return (rc == SQLITE_OK);
#else
#pragma unused(keyData)
    return NO;
#endif
}

FMDB使用setKey:和 setKeyWithData:输入密码和鉴别身份,通过rekey:和rekeyWithData:来清除和密码和设置密码,在上面的源码大家可以发现,也是对sqlite3_key以及sqlite3_rekey函数的封装。

上面就是FMDB中FMDatabase类的主要核心代码,希望大家对FMDatabase认识有个提高。

 

2.2 FMResultSet

2.2.1 初始化对象

+ (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB;

通过上面方法可以看出里面有两个参数

(1)第一个参数:(FMStatement *)statement

该对象是对sqlite3_stmt的进一步封装,在sqlite3_stmt *所表示的内容已经不是我们经常使用过的sql语句啦,而是预处理过的语句。

(2)第二个参数:(FMDatabase*)aDB

代表结果集所拥有的FMDatabase操作对象。

下面看一下初始化对象的实现代码

+ (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB {
    
    FMResultSet *rs = [[FMResultSet alloc] init];
    
    [rs setStatement:statement];
    [rs setParentDB:aDB];
    
    NSParameterAssert(![statement inUse]);
    [statement setInUse:YES]; // weak reference
    
    return FMDBReturnAutoreleased(rs);
}

 

2.2.2 遍历结果集合

- (BOOL)next;

 FMDB通过- (BOOL)next函数完成遍历取结果集合。一起看一下- (BOOL)next;的实现代码

- (BOOL)next {
    return [self nextWithError:nil];
}

上面代码可以发现:- (BOOL)next函数是对SQLite中-(BOOL)nextWithError:(NSError **)outErr函数的封装,主要完成对对象的逐行取值的任务。在深入看下nextWithError函数实现。

- (BOOL)nextWithError:(NSError **)outErr {
    
    int rc = sqlite3_step([_statement statement]);
    
    if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
        NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [_parentDB databasePath]);
        NSLog(@"Database busy");
        if (outErr) {
            *outErr = [_parentDB lastError];
        }
    }
    else if (SQLITE_DONE == rc || SQLITE_ROW == rc) {
        // all is well, let"s return.
    }
    else if (SQLITE_ERROR == rc) {
        NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle]));
        if (outErr) {
            *outErr = [_parentDB lastError];
        }
    }
    else if (SQLITE_MISUSE == rc) {
        // uh oh.
        NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle]));
        if (outErr) {
            if (_parentDB) {
                *outErr = [_parentDB lastError];
            }
            else {
                // If "next" or "nextWithError" is called after the result set is closed,
                // we need to return the appropriate error.
                NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:@"parentDB does not exist" forKey:NSLocalizedDescriptionKey];
                *outErr = [NSError errorWithDomain:@"FMDatabase" code:SQLITE_MISUSE userInfo:errorMessage];
            }
            
        }
    }
    else {
        // wtf?
        NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle]));
        if (outErr) {
            *outErr = [_parentDB lastError];
        }
    }
    
    
    if (rc != SQLITE_ROW) {
        [self close];
    }
    
    return (rc == SQLITE_ROW);
}

 

2.2.3 获取行列的值

通过查看源码发现有以下几处:

(1)- (int)intForColumnIndex:(int)columnIdx;

(2)- (long)longForColumnIndex:(int)columnIdx;

(3)- (long long int)longLongIntForColumnIndex:(int)columnIdx;

上面三个是根据列的索引获取该列的值。再看三个函数的实现代码

(1)- (int)intForColumnIndex:(int)columnIdx;

- (int)intForColumnIndex:(int)columnIdx {
    return sqlite3_column_int([_statement statement], columnIdx);
}

(2)- (long)longForColumnIndex:(int)columnIdx

- (long)longForColumnIndex:(int)columnIdx {
    return (long)sqlite3_column_int64([_statement statement], columnIdx);
}

(3)- (long long int)longLongIntForColumnIndex:(int)columnIdx

- (long long int)longLongIntForColumnIndex:(int)columnIdx {
    return sqlite3_column_int64([_statement statement], columnIdx);
}

通过上面三个函数,可以发现上面三个函数实际上是对sqlite3_column_ *函数的封装。

(4)- (int)intForColumn:(NSString*)columnName;

(5)- (long)longForColumn:(NSString*)columnName;

(6)- (long long int)longLongIntForColumn:(NSString*)columnName;

上面三个方法是根据列的名称取该列的值。下面看一下三个函数具体实现,只举一个即可,其他都是一样实现方式。

- (int)intForColumn:(NSString*)columnName {
    return [self intForColumnIndex:[self columnIndexForName:columnName]];
}

再深入看一下intForColumnIndex实现方式,回到上面啦,(方法(1)看上)。

- (int)intForColumnIndex:(int)columnIdx {
    return sqlite3_column_int([_statement statement], columnIdx);
}

 

2.2.4 获取行中所有元素

- (NSDictionary*)resultDictionary:返回值类型为字典。下面是实现方式:

- (NSDictionary*)resultDictionary {
    
    NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]);
    
    if (num_cols > 0) {
        NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols];
        
        int columnCount = sqlite3_column_count([_statement statement]);
        
        int columnIdx = 0;
        for (columnIdx = 0; columnIdx < columnCount; columnIdx++) {
            
            NSString *columnName = [NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)];
            id objectValue = [self objectForColumnIndex:columnIdx];
            [dict setObject:objectValue forKey:columnName];
        }
        
        return dict;
    }
    else {
        NSLog(@"Warning: There seem to be no columns in this set.");
    }
    
    return nil;
}

 

2.2.5 KVC讲解

- (void)kvcMagic:(id)object:FMDB中只能对string类型进行支持

下面是kvcMagic:(id)object实现方式

- (void)kvcMagic:(id)object {
   // 使用了KVC,将数据库中的每一行数据对应到每一个对象中,对象的属性要和数据库的列名保持一直。
    int columnCount = sqlite3_column_count([_statement statement]);
    
    int columnIdx = 0;
    for (columnIdx = 0; columnIdx < columnCount; columnIdx++) {
        
        const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx);
        
        // check for a null row
        if (c) {
            NSString *s = [NSString stringWithUTF8String:c];
            
            [object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]];
        }
    }
}

 

2.3 FMDatabaseQueue

FMDB中比较突出优点就是对多线程的处理,而FMDB中对多线程的支持多亏FMDatabaseQueue类。

2.3.1 初始化队列

+ (instancetype)databaseQueueWithPath:(NSString*)aPath

实现代码如下:

+ (instancetype)databaseQueueWithPath:(NSString *)aPath {
    FMDatabaseQueue *q = [[self alloc] initWithPath:aPath];
    
    FMDBAutorelease(q);
    
    return q;
}

上面函数调用了- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName函数实现如下:

- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName {
    self = [super init];
    
    if (self != nil) {
        
        _db = [[[self class] databaseClass] databaseWithPath:aPath];
        FMDBRetain(_db);
        
#if SQLITE_VERSION_NUMBER >= 3005000
        BOOL success = [_db openWithFlags:openFlags vfs:vfsName];
#else
        BOOL success = [_db open];
#endif
        if (!success) {
            NSLog(@"Could not create database queue for path %@", aPath);
            FMDBRelease(self);
            return 0x00;
        }
        
        _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 = [vfsName copy];
    }
    
    return self;
}    

 

2.3.2 串行执行数据库的操作

下面看一段代码

- (void)inDatabase:(void (^)(FMDatabase *db))block {
    /* 使用dispatch_get_specific目的来查看当前queue是否?

编辑:马邓道马

发布:2018-09-20 04:44:15

当前文章:http://leetaemin.cn/array/ii6c5mxof9.html

投资赚钱的名言 最新微信暴利赚钱项目 中网赚论坛 好省怎么赚钱 派派游戏的钱是真的吗 做什么微商代理最赚钱 重庆时时彩微信群谁有 赚客全自动挂机软件

19730 63059 74317 50246 42444 1373863247 60670 73805

我要说两句: (0人参与)

发布