本文使用第一人称,采用fmdb自述的方式,将有关fmdb的一些常用API和实现原理一一道来。请放心,简短精准的描述下,你无需担心阅读时长。
先奉上官方权威文档:
FMDB Reference
SQLite Home Page
英语过4级的同学强烈建议去阅读官方的文档,原汁原味!!!
One section:fmdb Introduction
hello,everybody!I am fmdb. Welcome to my share party.I don't like speech because I am a shy boy. The way of share I like is you ask and I answer.So,when I speech then if you have some questions please let me know when I end .OK?
OK.But first let's see some images about me .Believe me.It's better for you to look them before listen
wow,so many!!Today is a good day!!OK.Don't worry, you just need less 1/5 to finish your job.
Tow section:fmdb basic use
OK ,Here have a good example about me # LGDatabaseCacheProgram
Before you begin ,you should consideration one important thing --'thread safe' .There is a sentence from FMDatabase--'Do not instantiate a single FMDatabase
object and use it across multiple threads. Instead, use
.'
So,if you need add a table from db, you should use following method
NSArray*paths=NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES);
NSString*path=[paths objectAtIndex:0];
NSString*db = [path stringByAppendingPathComponent:CACHEUSER];
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:db];
If you don't know Sandbox,this article will help you iOS沙盒之基本概念.
Next, you can use this queue to finish add delete and change
But,don't anxious.we should first know what is FMDatabaseQueue
To perform queries and updates on multiple threads, you'll want to use `FMDatabaseQueue`.
Using a single instance of `` from multiple threads at once is a bad idea. It has always been OK to make a `` object *per thread*. Just don't share a single instance across threads, and definitely not across multiple threads at the same time.
First, make your queue.
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
Then use it like so:
[queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
FMResultSet *rs = [db executeQuery:@"select * from foo"];
while ([rs next]) {
//…
}
}];
An easy way to wrap things up in a transaction can be done like this:
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
if (whoopsSomethingWrongHappened) {
*rollback = YES;
return;
}
// etc…
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]];
}];
`FMDatabaseQueue` will run the blocks on a serialized[ˈsɪəri:əˌlaɪzd] queue (hence the name of the class).
So if you call `FMDatabaseQueue`'s methods from multiple[ˈməltəpəl] threads at the same time, they will be executed [ˈeksikju:tid] in the order they are received.
This way queries and updates won't step on each other's toes, and every one is happy
Initialization
I will give you some convenient method for get a object about me.But actually,they all depend one method。
- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName {
self = [super init];
if (self != nil) {
/**
[self class]:获取类名,你应该思考一下这么写的用意!!!
[[self class] databaseClass] :获取FMDatabase或者其子类。
(databaseClass ==>返回'FMDatabase'子类,它将用于实例化数据库对象。子类可以重写此方法以返回指定的“FMDatabase”子类。)
*/
_db = [[[self class] databaseClass] databaseWithPath:aPath];
/**
宏,用来兼容ARC和MRC。这里,你应该看一下这个宏的定义
*/
FMDBRetain(_db);
#if SQLITE_VERSION_NUMBER >= 3005000
BOOL success = [_db openWithFlags:openFlags vfs:vfsName];
#else
BOOL success = [_db open];
#endif
/**
对数据库打开结果做了判断。。。
*/
if (!success) {
NSLog(@"Could not create database queue for path %@", aPath);
FMDBRelease(self);
return 0x00;
}
/**
友情提示:如果你还不知道MRC,其先百度!!
*/
_path = FMDBReturnRetained(aPath);
/**创建一个队列
dispatch_queue_t dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);
参数:const char *_Nullable label:label表示该队列的唯一标识字符串
dispatch_queue_attr_t _Nullable attr:DISPATCH_QUEUE_SERIAL //指定串行(FIFO)队列,等同于传入参数NULL
DISPATCH_QUEUE_CONCURRENT //指定并发队列,
*/
///串行队列来管理任务
_queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
/**
dispatch_queue_set_specific就是向指定队列里面设置一个标识 如:
dispatch_queue_set_specific(queue1, queueKey1, &queueKey1,NULL);就是向queue1对了中设置一个queueKey1标识。
dispatch_get_specific就是在当前队列中取出标识,注意iOS中线程和队列的关系,所有的动作都是在队列中执行的!
*/
///添加此标记用来防止串行队列常见的死锁问题
dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL);
/**
面试常见提问:深拷贝和浅拷贝
*/
_openFlags = openFlags;
_vfsName = [vfsName copy];
}
return self;
}