FMDB源码阅读(二)FMDatabase.m
FMDatabase.m
1 @interface FMDatabase () { 2 void* _db; 3 BOOL _isExecutingStatement; 4 NSTimeInterval _startBusyRetryTime; 5 6 NSMutableSet *_openResultSets; 7 NSMutableSet *_openFunctions; 8 9 NSDateFormatter *_dateFormat; 10 }
成员变量。
1 NS_ASSUME_NONNULL_BEGIN 2 3 - (FMResultSet * _Nullable)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray * _Nullable)arrayArgs orDictionary:(NSDictionary * _Nullable)dictionaryArgs orVAList:(va_list)args; 4 - (BOOL)executeUpdate:(NSString *)sql error:(NSError * _Nullable *)outErr withArgumentsInArray:(NSArray * _Nullable)arrayArgs orDictionary:(NSDictionary * _Nullable)dictionaryArgs orVAList:(va_list)args; 5 6 NS_ASSUME_NONNULL_END
两个很长的执行数据库更新和查询的方法。
1 // Because these two properties have all of their accessor methods implemented, 2 // we have to synthesize them to get the corresponding ivars. The rest of the 3 // properties have their ivars synthesized automatically for us. 4 5 @synthesize shouldCacheStatements = _shouldCacheStatements; 6 @synthesize maxBusyRetryTimeInterval = _maxBusyRetryTimeInterval;
给成员变量指定新的名字(是否缓存 SQL 语句和描述即将到来的)。
数据库实例化和存储单元分配:
1 + (instancetype)databaseWithPath:(NSString *)aPath { 2 return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]); 3 } 4 5 + (instancetype)databaseWithURL:(NSURL *)url { 6 return FMDBReturnAutoreleased([[self alloc] initWithURL:url]); 7 } 8 9 - (instancetype)init { 10 return [self initWithPath:nil]; 11 } 12 13 - (instancetype)initWithURL:(NSURL *)url { 14 return [self initWithPath:url.path]; 15 } 16 17 - (instancetype)initWithPath:(NSString *)path { 18 19 assert(sqlite3_threadsafe()); // whoa there big boy- gotta make sure sqlite it happy with what we're going to do. 20 21 self = [super init]; 22 23 if (self) { 24 _databasePath = [path copy]; 25 _openResultSets = [[NSMutableSet alloc] init]; 26 _db = nil; 27 _logsErrors = YES; 28 _crashOnErrors = NO; 29 _maxBusyRetryTimeInterval = 2; 30 } 31 32 return self; 33 }
指定路径初始化数据库。
1 #define assert(e) \ 2 (__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__, __LINE__, #e) : (void)0)
1 #if ! __has_feature(objc_arc) 2 - (void)finalize { 3 [self close]; 4 [super finalize]; 5 } 6 #endif 7 8 - (void)dealloc { 9 [self close]; 10 FMDBRelease(_openResultSets); 11 FMDBRelease(_cachedStatements); 12 FMDBRelease(_dateFormat); 13 FMDBRelease(_databasePath); 14 FMDBRelease(_openFunctions); 15 16 #if ! __has_feature(objc_arc) 17 [super dealloc]; 18 #endif 19 }
内存释放,兼容 ARC 和 MRC。
1 - (NSURL *)databaseURL { 2 return _databasePath ? [NSURL fileURLWithPath:_databasePath] : nil; 3 } 4 5 + (NSString*)FMDBUserVersion { 6 return @"2.7.2"; 7 }
返回路径和返回 FMDB 使用版本。
1 // returns 0x0240 for version 2.4. This makes it super easy to do things like: 2 // /* need to make sure to do X with FMDB version 2.4 or later */ 3 // if ([FMDatabase FMDBVersion] >= 0x0240) { … } 4 5 + (SInt32)FMDBVersion { 6 7 // we go through these hoops so that we only have to change the version number in a single spot. 8 static dispatch_once_t once; 9 static SInt32 FMDBVersionVal = 0; 10 11 dispatch_once(&once, ^{ 12 NSString *prodVersion = [self FMDBUserVersion]; 13 14 if ([[prodVersion componentsSeparatedByString:@"."] count] < 3) { 15 prodVersion = [prodVersion stringByAppendingString:@".0"]; 16 } 17 18 NSString *junk = [prodVersion stringByReplacingOccurrencesOfString:@"." withString:@""]; 19 20 char *e = nil; 21 FMDBVersionVal = (int) strtoul([junk UTF8String], &e, 16); 22 23 }); 24 25 return FMDBVersionVal; 26 }
返回 FMDB 版本,补0去处英语句号返回一个无符号 long。
1 unsigned long 2 strtoul(const char *__str, char **__endptr, int __base);
SQLite 的信息:
1 + (NSString*)sqliteLibVersion { 2 return [NSString stringWithFormat:@"%s", sqlite3_libversion()]; 3 } 4 5 + (BOOL)isSQLiteThreadSafe { 6 // make sure to read the sqlite headers on this guy! 7 return sqlite3_threadsafe() != 0; 8 } 9 10 - (void*)sqliteHandle { 11 return _db; 12 } 13 14 - (const char*)sqlitePath { 15 16 if (!_databasePath) { 17 return ":memory:"; 18 } 19 20 if ([_databasePath length] == 0) { 21 return ""; // this creates a temporary database (it's an sqlite thing). 22 } 23 24 return [_databasePath fileSystemRepresentation]; 25 26 }
SQLiteLit 版本、SQLite 是否是线程安全的、返回数据库、返回三条路径之一。
打开和关闭数据库:
1 - (BOOL)open { 2 if (_db) { 3 return YES; 4 } 5 6 int err = sqlite3_open([self sqlitePath], (sqlite3**)&_db ); 7 if(err != SQLITE_OK) { 8 NSLog(@"error opening!: %d", err); 9 return NO; 10 } 11 12 if (_maxBusyRetryTimeInterval > 0.0) { 13 // set the handler 14 [self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval]; 15 } 16 17 18 return YES; 19 }
1 - (BOOL)openWithFlags:(int)flags { 2 return [self openWithFlags:flags vfs:nil]; 3 } 4 - (BOOL)openWithFlags:(int)flags vfs:(NSString *)vfsName { 5 #if SQLITE_VERSION_NUMBER >= 3005000 6 if (_db) { 7 return YES; 8 } 9 10 int err = sqlite3_open_v2([self sqlitePath], (sqlite3**)&_db, flags, [vfsName UTF8String]); 11 if(err != SQLITE_OK) { 12 NSLog(@"error opening!: %d", err); 13 return NO; 14 } 15 16 if (_maxBusyRetryTimeInterval > 0.0) { 17 // set the handler 18 [self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval]; 19 } 20 21 return YES; 22 #else 23 NSLog(@"openWithFlags requires SQLite 3.5"); 24 return NO; 25 #endif 26 }
1 SQLITE_API int SQLITE_STDCALL sqlite3_open( 2 const char *filename, /* Database filename (UTF-8) */ 3 sqlite3 **ppDb /* OUT: SQLite db handle */ 4 ); 5 SQLITE_API int SQLITE_STDCALL sqlite3_open16( 6 const void *filename, /* Database filename (UTF-16) */ 7 sqlite3 **ppDb /* OUT: SQLite db handle */ 8 ); 9 SQLITE_API int SQLITE_STDCALL sqlite3_open_v2( 10 const char *filename, /* Database filename (UTF-8) */ 11 sqlite3 **ppDb, /* OUT: SQLite db handle */ 12 int flags, /* Flags */ 13 const char *zVfs /* Name of VFS module to use */ 14 );
携带标志打开数据库。
1 - (BOOL)close { 2 3 [self clearCachedStatements]; 4 [self closeOpenResultSets]; 5 6 if (!_db) { 7 return YES; 8 } 9 10 int rc; 11 BOOL retry; 12 BOOL triedFinalizingOpenStatements = NO; 13 14 do { 15 retry = NO; 16 rc = sqlite3_close(_db); 17 if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { 18 if (!triedFinalizingOpenStatements) { 19 triedFinalizingOpenStatements = YES; 20 sqlite3_stmt *pStmt; 21 while ((pStmt = sqlite3_next_stmt(_db, nil)) !=0) { 22 NSLog(@"Closing leaked statement"); 23 sqlite3_finalize(pStmt); 24 retry = YES; 25 } 26 } 27 } 28 else if (SQLITE_OK != rc) { 29 NSLog(@"error closing!: %d", rc); 30 } 31 } 32 while (retry); 33 34 _db = nil; 35 return YES; 36 }
清除缓存的数据库语句。
关闭返回的结果。
1 #pragma mark Busy handler routines 2 3 // NOTE: appledoc seems to choke on this function for some reason; 4 // so when generating documentation, you might want to ignore the 5 // .m files so that it only documents the public interfaces outlined 6 // in the .h files. 7 // 8 // This is a known appledoc bug that it has problems with C functions 9 // within a class implementation, but for some reason, only this 10 // C function causes problems; the rest don't. Anyway, ignoring the .m 11 // files with appledoc will prevent this problem from occurring. 12 13 static int FMDBDatabaseBusyHandler(void *f, int count) { 14 FMDatabase *self = (__bridge FMDatabase*)f; 15 16 if (count == 0) { 17 self->_startBusyRetryTime = [NSDate timeIntervalSinceReferenceDate]; 18 return 1; 19 } 20 21 NSTimeInterval delta = [NSDate timeIntervalSinceReferenceDate] - (self->_startBusyRetryTime); 22 23 if (delta < [self maxBusyRetryTimeInterval]) { 24 int requestedSleepInMillseconds = (int) arc4random_uniform(50) + 50; 25 int actualSleepInMilliseconds = sqlite3_sleep(requestedSleepInMillseconds); 26 if (actualSleepInMilliseconds != requestedSleepInMillseconds) { 27 NSLog(@"WARNING: Requested sleep of %i milliseconds, but SQLite returned %i. Maybe SQLite wasn't built with HAVE_USLEEP=1?", requestedSleepInMillseconds, actualSleepInMilliseconds); 28 } 29 return 1; 30 } 31 32 return 0; 33 }
1 - (void)setMaxBusyRetryTimeInterval:(NSTimeInterval)timeout { 2 3 _maxBusyRetryTimeInterval = timeout; 4 5 if (!_db) { 6 return; 7 } 8 9 if (timeout > 0) { 10 sqlite3_busy_handler(_db, &FMDBDatabaseBusyHandler, (__bridge void *)(self)); 11 } 12 else { 13 // turn it off otherwise 14 sqlite3_busy_handler(_db, nil, nil); 15 } 16 } 17 18 - (NSTimeInterval)maxBusyRetryTimeInterval { 19 return _maxBusyRetryTimeInterval; 20 }
重写 maxBusyRetryTimeInterval 的 set 和 get 方法。
1 // we no longer make busyRetryTimeout public 2 // but for folks who don't bother noticing that the interface to FMDatabase changed, 3 // we'll still implement the method so they don't get suprise crashes 4 - (int)busyRetryTimeout { 5 NSLog(@"%s:%d", __FUNCTION__, __LINE__); 6 NSLog(@"FMDB: busyRetryTimeout no longer works, please use maxBusyRetryTimeInterval"); 7 return -1; 8 } 9 10 - (void)setBusyRetryTimeout:(int)i { 11 #pragma unused(i) 12 NSLog(@"%s:%d", __FUNCTION__, __LINE__); 13 NSLog(@"FMDB: setBusyRetryTimeout does nothing, please use setMaxBusyRetryTimeInterval:"); 14 }
busyRetryTimeout 的 set 方法和 get 方法。
结果集合功能:
1 - (BOOL)hasOpenResultSets { 2 return [_openResultSets count] > 0; 3 }
是否有结果集。
1 - (void)closeOpenResultSets { 2 3 //Copy the set so we don't get mutation errors 4 NSSet *openSetCopy = FMDBReturnAutoreleased([_openResultSets copy]); 5 for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) { 6 FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue]; 7 8 [rs setParentDB:nil]; 9 [rs close]; 10 11 [_openResultSets removeObject:rsInWrappedInATastyValueMeal]; 12 } 13 }
关闭结果集。
1 - (void)resultSetDidClose:(FMResultSet *)resultSet { 2 NSValue *setValue = [NSValue valueWithNonretainedObject:resultSet]; 3 4 [_openResultSets removeObject:setValue]; 5 }
缓存报表:
1 - (void)clearCachedStatements { 2 3 for (NSMutableSet *statements in [_cachedStatements objectEnumerator]) { 4 for (FMStatement *statement in [statements allObjects]) { 5 [statement close]; 6 } 7 } 8 9 [_cachedStatements removeAllObjects]; 10 }
清除缓存的数据库语句。
1 - (FMStatement*)cachedStatementForQuery:(NSString*)query { 2 3 NSMutableSet* statements = [_cachedStatements objectForKey:query]; 4 5 return [[statements objectsPassingTest:^BOOL(FMStatement* statement, BOOL *stop) { 6 7 *stop = ![statement inUse]; 8 return *stop; 9 10 }] anyObject]; 11 }
怎么样成为程序员,学习和实践,日积月累...