FMDB是IOS平臺的SQLite數據庫框架,以OC的方式封裝了SQLite的C語言的API。FMDB使用起來更加的面向對象,省去了不少麻煩、冗餘的C語言代碼具體對比詳見個人簡書iOS開發數據存儲篇—libsqlite3和FMDB的基本使用和區別,對比蘋果自帶的Core Data框架,更加的輕量級和靈活。提供了多線程安全的數據庫操做的方法,有效的防止數據混亂。開源地址爲github.com/ccgus/fmdb。git
這是一個個人iOS交流羣:624212887,羣文件自行下載,無論你是小白仍是大牛熱烈歡迎進羣 ,分享面試經驗,討論技術, 你們一塊兒交流學習成長!但願幫助開發者少走彎路。——點擊:加入github
FMDB源碼主要有如下幾個文件組成:面試
FMResultSet : 表示FMDatabase執行查詢以後的結果集。sql
FMDatabase : 表示一個單獨的SQLite數據庫操做實例,經過它能夠對數據庫進行增刪改查等等操做。數據庫
FMDatabaseAdditions : 擴展FMDatabase類,新增對查詢結果只返回單個值的方法進行簡化,對錶、列是否存在,版本號,校驗SQL等等功能。安全
FMDatabaseQueue : 使用串行隊列 ,對多線程的操做進行了支持。bash
FMDatabasePool : 使用任務池的形式,對多線程的操做提供支持。(不過官方對這種方式並不推薦使用,優先選擇FMDatabaseQueue的方式:ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD)網絡
下面咱們就來逐個分析FMDB源碼的實現方式,先講FMResultSet的實現思路。數據結構
該對象主要是對sqlite3_stmt的封裝,sqlite3_stmt * 所表示的內容能夠當作是預處理過得sql語句,已經不是咱們熟知的sql語句。他是一個已經把sql語句解析了,用sqlite本身表示記錄的內部數據結構。多線程
該結果集所屬於的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];
return FMDBReturnAutoreleased(rs);
}
複製代碼
-(BOOL)next;實際上是對-(BOOL)nextWithError:(NSError **)outErr;函數的封裝。主要做用是經過sqlite3_step函數對FMStatement中的sqlite3_stmt對象進行逐行取值。
/**
* 遍歷每一行的數據(fmdb:next() --》c:sqlite3_step() )
*
* @param outErr 錯誤信息
*
* @return
*/
- (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); } 複製代碼
/**
* 列的名稱與索引的一一對應關係
*
* @return
* @{ @「id」:@0,
* @"name":@1,
* @"age":@2
* }
*/
- (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;
}
複製代碼
-(int)columnIndexForName:(NSString*)columnName; 根據列名獲取該列的所在第幾列(列的索引)
-(NSString*)columnNameForIndex:(int)columnIdx;根據列的索引獲取該列的名稱。
-XXXForColumnIndex:(int)columnIdx;實際上是對sqlite3_column_*函數的封裝。以下所示。
- (int)intForColumnIndex:(int)columnIdx {
return sqlite3_column_int([_statement statement], columnIdx);
}
複製代碼
由2.1.3中columnNameToIndexMap咱們能夠獲得列名與索引的一一對象關係,那麼-XXXForColumn:(NSString*)columnName;的實現就很簡單了。
/**
* 根據列的名稱獲取int值
*
* @param columnName
*
* @return
*/
- (int)intForColumn:(NSString*)columnName {
return [self intForColumnIndex:[self columnIndexForName:columnName]];
}
複製代碼
/**
* 每一行數據的結果所對應的Dictionary
*
* @return
* @{
* age = 29;
* id = 1;
* name = "yixiang-20";
* }
*/
- (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;
}
複製代碼
FMDB這裏的支持仍是比較簡單的,只能對於String類型的屬性進行支持。
/**
* 使用KVC,把數據庫中的每一行數據對應到每個對象,對象的屬性要和數據庫的列名保持一直。
*
* @param object 對象
*/
- (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)]];
}
}
}
複製代碼
這是一個個人iOS交流羣:624212887,羣文件自行下載,無論你是小白仍是大牛熱烈歡迎進羣 ,分享面試經驗,討論技術, 你們一塊兒交流學習成長!但願幫助開發者少走彎路。——點擊:加入
若是以爲對你還有些用,就關注小編+喜歡這一篇文章。你的支持是我繼續的動力。
下篇文章預告:·FMDB源碼分析2(FMDatabase+FMDatabaseAdditions)
文章來源於網絡,若有侵權,請聯繫小編刪除。