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 }

 

posted @ 2017-06-26 02:09  鳄鱼不怕牙医不怕  阅读(394)  评论(0编辑  收藏  举报