上一篇讲解了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;
- 方法中还有一系列的 <type>ForColumn:与 <type>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结束之后,这样获取到的值都是有问题的。