FMDB源码系列(二)

上一篇讲解了FMDB使用的一些宏与Sqlite3错误码,都是FMDB中常用的。这篇我们开始进入源码的解读过程。

FMResultSet.h

其中常用的方法与属性有这些:

  • 属性
//该结果集的DB实例对象
@property (nonatomic, retain, nullable) FMDatabase *parentDB;
//查询出该结果集所使用的SQL语句
@property (atomic, retain, nullable) NSString *query;
//该结果集的列映射出的数字索引
@property (readonly) NSMutableDictionary *columnNameToIndexMap;
//查询出的结果集的行数
@property (nonatomic, readonly) int columnCount;
//当前结果集中所在的行内数据映射的字典,列名区分大小写
@property (nonatomic, readonly, nullable) NSDictionary *resultDictionary;
  • 方法
//关闭该结果集
- (void)close;
//遍历结果集
- (BOOL)next;
- (BOOL)nextWithError:(NSError * _Nullable *)outErr;
//跟随next方法使用,判断执行Next方法时是否成功检索到另一行
- (BOOL)hasAnotherRow;
//通过FMStatement对象与FMDatabase对象来查询一个结果集,自动打开关闭数据库。
+ (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB;
//根据索引返回该索引的列名
- (NSString * _Nullable)columnNameForIndex:(int)columnIdx;
//判断传入的索引值对应的列是否存在
- (BOOL)columnIndexIsNull:(int)columnIdx;
//判断传入的列名对应的列是否存在
- (BOOL)columnIsNull:(NSString*)columnName;
//将结果集所对行的数据,使用KVC给对象赋值,对象中的属性名要与列名一致。
- (void)kvcMagic:(id)object;
  • 方法中还有一系列的 ForColumn:与 ForColumnIndex:方法,用来取值,如 :
- (int)intForColumn:(NSString*)columnName;
- (int)intForColumnIndex:(int)columnIdx;
- (long)longForColumn:(NSString*)columnName;
- (long)longForColumnIndex:(int)columnIdx;

关于该类取值方法,以intForColumn:与intForColumnIndex:方法为例。
其中intForColumn:的实现方法实际是调用了intForColumnIndex:方法取值的:

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

而intForColumnIndex:方法是以使用sqlite3方法来取值的:

- (int)intForColumnIndex:(int)columnIdx {
    return sqlite3_column_int([_statement statement], columnIdx);
}
  • 再来看- (int)columnIndexForName:(NSString*)columnName方法的实现
- (int)columnIndexForName:(NSString*)columnName {
    columnName = [columnName lowercaseString];
    
    NSNumber *n = [[self columnNameToIndexMap] objectForKey:columnName];
    
    if (n != nil) {
        return [n intValue];
    }
    
    NSLog(@"Warning: I could not find the column named '%@'.", columnName);
    
    return -1;
}

实现过程很简单,就是根据列名的数组映射,拿到该列的索引值。


  • 接着来看下取名很艺术的- (void)kvcMagic:(id)object 方法,看完就知道,它的实现并没有那么Magic:
- (void)kvcMagic:(id)object {
    //用sqlite3方法取出结果集蕴含的行数
    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);
        //在列名不为空的情况下,使用KVC给Object的属性赋值。
        // check for a null row
        if (c) {
            NSString *s = [NSString stringWithUTF8String:c];
            
            [object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]];
        }
    }
}

这个方法,只能给NSString类型的属性赋值,实在是令人对这个Magic有些失望。

  • 最后来看一下next方法,next方法是以nextWithError:来实现的,该方法分为3部分:
    首先执行sqlite3方法 sqlite3_step 来遍历结果集,以rc来接收返回的状态值。
int rc = sqlite3_step([_statement statement]);

然后根据rc的值来判断异常(报异常的代码已省略),错误码请对照上一篇。

if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
      //报异常
    }
    else if (SQLITE_DONE == rc || SQLITE_ROW == rc) {
        // all is well, let's return.一切正常
    }
    else if (SQLITE_ERROR == rc) {
             //报异常
    }
    else if (SQLITE_MISUSE == rc) {
            //报异常
    }
    else {
           //报异常
    }

最后做终止判断以及返回当前结果

    if (rc != SQLITE_ROW) {
        [self close];
    }
    
    return (rc == SQLITE_ROW);
  • 最后,需要注意的是因为next方法执行到最后,会关闭该结果集。所以当代码中需要获取一些结果集对象的属性时,请不要放在next结束之后,这样获取到的值都是有问题的。

你可能感兴趣的:(FMDB源码系列(二))