基于目前项目中老是遇到fmdb的crash,网上找一圈以后也没有找到思路,因此有了读源码的冲动,但愿在过程当中能分析出问题的所在,下降crash率。php
本文以类做为突破口。过程当中可能会对系统sqlite3的部分地方进行简单分析。html
FMDB源码主要有一下几个文件组成:sql
FMDatabase:
表示一个单独的SQLite DB实例,经过它能够对数据库进行增删改查等操做。数据库
FMResultSet:
表示经过sql在DB中查询到的结果集,而且将查询结果转化成对应的值或对象,例如:int、long、bool、NSString、NSDate、NSData、char *、 id等。bash
FMDatabaseQueue:
用来管理数据查询的队列,保证大部分时间下对数据库的操做是串行的。app
FMDatabaseAdditions:
做为 FMDatabase
类的拓展。新增了一些经常使用的校验方法,例如:表是否存在、列是否存在、版本号、sql校验等。ui
FMDatabasePool:
用来管理数据库查询任务。不过在头文件中,做者写的很是清楚墙裂不建议使用,而是用 FMDatabaseQueue
代替。若是必定要用的话,必定要注意死锁。this
+ (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);
}
复制代码
参数1:
FMStatement
spa
sqlite3_stmt
的封装,在使用的时候通常你们不会直接使用 FMStatement
,只须要 FMDatabase
和 FMStatement
交互;sqlite3_stmt
还拓展了 query
、useCount
、inUse
字段,并实现了 statement
的 close
和 reset
方法。拓展:sqlite3_stmt--> http://www.sqlite.org/c3ref/stmt.html
* sqlite3_stmt 表示已编为二进制形式且能够进行执行的单个SQL语句。
* 把每一个SQL语句看做一个单独的计算机程序。原始的SQL文本是源代码。准备好的statement是已编译的对象代码。必须先将全部SQL转换为准备好的statement,而后才能运行它。
* 生命周期
1.sqlite3_prepare_v2() 建立对象
2.sqlite3_bind_*() Bind values to parameters
3.sqlite3_step() 执行sql
4.sqlite3_reset() 重置statement,而后执行第2步
5.sqlite3_finalize() 销毁对象
复制代码
参数2:FMDatabasecode
result
查询的数据库- (BOOL)next {
int rc = sqlite3_step([_statement statement]); //执行sql,返回状态
if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [_parentDB databasePath]);
NSLog(@"Database busy");
}
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])); } else if (SQLITE_MISUSE == rc) { // uh oh. NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); } else { // wtf? NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); } if (rc != SQLITE_ROW) { [self close]; } return (rc == SQLITE_ROW);//sqlite3_step() has another row ready } 复制代码
int rc = sqlite3_step([_statement statement]);
执行sql,返回状态SQLITE_BUSY
和 SQLITE_LOCKED
数据库被锁或者数据库忙碌SQLITE_DONE
和 SQLITE_ROW
执行完毕,没有发生错误SQLITE_ERROR
通用错误SQLITE_MISUSE
-->Library used incorrectly
库错SQLITE_ROW
若是下一行没有reday,则直接关闭,因此在使用while([rs next])的时候不须要手动关闭- (void)close {
[_statement reset];
FMDBRelease(_statement);
_statement = nil;
// we don't need this anymore... (i think) //[_parentDB setInUse:NO]; [_parentDB resultSetDidClose:self]; [self setParentDB:nil]; } 复制代码
重置statement,将statement对象置nil,释放当前db对象
- (NSMutableDictionary *)columnNameToIndexMap {
if (!_columnNameToIndexMap) {
int columnCount = sqlite3_column_count([_statement statement]);
_columnNameToIndexMap = [[NSMutableDictionary alloc] initWithCapacity:(NSUInteger)columnCount];
int columnIdx = 0;
for (columnIdx = 0; columnIdx < columnCount; columnIdx++) {
[_columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx]
forKey:[[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)] lowercaseString]];
}
}
return _columnNameToIndexMap;
}
复制代码
statement
获取到返回的列总数N,并建立一个默认N个对象的字典lowercaseString
做为key值,绑定列的index。- (void)kvcMagic:(id)object {
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)]];
}
}
}
复制代码
-XXXForColumnIndex:(int)columnIdx;根据列的索引获取该列的值。
-XXXForColumn:(NSString*)columnName;根据列的名称获取该列的值。
- (NSString*)stringForColumnIndex:(int)columnIdx {
if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
return nil;
}
const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx);
if (!c) {
// null row.
return nil;
}
return [NSString stringWithUTF8String:c];
}
复制代码
拓展:——> http://www.sqlite.org/c3ref/column_blob.html
sqlite3_column_blob → BLOB result
sqlite3_column_double → REAL result
sqlite3_column_int → 32-bit INTEGER result
sqlite3_column_int64 → 64-bit INTEGER result
sqlite3_column_text → UTF-8 TEXT result
sqlite3_column_text16 → UTF-16 TEXT result
sqlite3_column_value → The result as an unprotected sqlite3_value object.
sqlite3_column_bytes → Size of a BLOB or a UTF-8 TEXT result in bytes
sqlite3_column_bytes16 → Size of UTF-16 TEXT in bytes
sqlite3_column_type → Default datatype of the result
复制代码
根据列名获取该列的值
- (NSString*)stringForColumn:(NSString*)columnName {
return [self stringForColumnIndex:[self columnIndexForName:columnName]];
}
复制代码
- (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;
}
复制代码
sqlite3_data_count(P)
returns the number of columns in the current row of the result set of [prepared statement] P1.因为替换最新版fmdb后crash增多,为保证工程稳定性恢复至历史版本,所以分析的大部分源码会贴至文章。
2.欢迎你们对文章给出建议或意见。
3.本文凝结了做者的心血,但愿你们在转发、传阅的时候可以保留文章的初始地址。
参考连接: