概念:

  FMDB 是用于数据存储的框架,它是 iOS 平台下对 SQLite 数据库的封装。FMDB 是面向对象的,它以 OC 的方式封装了 SQLite 的 C 语言 API,使用起来更加方便。

  Core Data是 ORM(对象关系映射) 的一种体现,使用Core Data需要用到模型数据的转化,虽然操作简单,不需要直接操作数据库,但是性能没有直接使用SQLite高。但是SQLite使用的时候需要使用c语言中的函数,操作比较麻烦,因此需要对它进行封装。但是如果只是简单地封装,很可能会忽略很多重要的细节,比如如何处理并发以及安全性更问题。

  使用第三方框架FMDB,它是对libsqlite3框架的封装,用起来的步骤与SQLite使用类似,并且它对于多线程的同时操作一个表格时进行了处理,也就意味着它是线程安全的。FMDB是轻量级的框架,使用灵活,它是很多企业开发的首选。

  FMDB GitHub下载地址:

  重要的类:

  1. FMResultSet : 表示FMDatabase执行查询之后的结果集。

  2. FMDatabase : 表示一个单独的SQLite数据库操作实例,用来执行SQL语句, 通过它可以对数据库进行增删改查等等操作。

  3. FMDatabaseAdditions : 扩展FMDatabase类,新增对查询结果只返回单个值的方法进行简化,对表、列是否存在,版本号,校验SQL等等功能。

  4. FMDatabaseQueue : 使用串行队列 ,对多线程的操作进行了支持,用于在多线程中执行多个查询或更新,它是线程安全的。

  5. FMDatabasePool : 使用任务池的形式,对多线程的操作提供支持。(不过官方对这种方式并不推荐使用,优先选择FMDatabaseQueue的方式:ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD)

  FMDatabaseQueue 要使用单例创建,这样多线程调用时,数据库操作使用一个队列,保证线程安全。

  是把数据库的操作放到一个串行队列中,从而保证不会在同一时间对数据库做改动。

  多线程下使用FMDatabaseQueue的操作原理就可以创建一个管理类对模型数据的存取查删进行统一管理,可以使用工具类操作,也可以创建集成NSObject的子类进行管理,需要存取的模型类继承此子类即可。

  FMDatabaseQueue如何实现多线程?

/**
* FMDatabaseQueue如何实现多线程的案例
*/
- (void)FMDatabaseQueueMutilThreadTest {
//1、获取数据库文件路径
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileName = [doc stringByAppendingPathComponent:@"students.sqlite"];
//使用queue1
FMDatabaseQueue *queue1 = [FMDatabaseQueue databaseQueueWithPath:fileName];
[queue1 inDatabase:^(FMDatabase *db) {
for (int i=; i<; i++) {
NSLog(@"queue1---%zi--%@",i,[NSThread currentThread]);
}
}]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
[queue1 inDatabase:^(FMDatabase *db) {
for (int i=; i<; i++) {
NSLog(@"queue1---%zi--%@",i,[NSThread currentThread]);
}
}];
}); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
[queue1 inDatabase:^(FMDatabase *db) {
for (int i=; i<; i++) {
NSLog(@"queue1---%zi--%@",i,[NSThread currentThread]);
}
}];
}); //虽然开启了多个线程,可依然还是串行处理。原因如下: /**FMDatabaseQueue虽然看似一个队列,实际上它本身并不是,它通过内部创建一个Serial的dispatch_queue_t来处理通过inDatabase和inTransaction传入的Blocks,所以当我们在主线程(或者后台)调用inDatabase或者inTransaction时,代码实际上是同步的。FMDatabaseQueue这么设计的目的是让我们避免发生并发访问数据库的问题,因为对数据库的访问可能是随机的(在任何时候)、不同线程间(不同的网络回调等)的请求。内置一个Serial队列后,FMDatabaseQueue就变成线程安全了,所有的数据库访问都是同步执行,而且这比使用@synchronized或NSLock要高效得多。
*/
}

  

  //虽然开启了多个线程,可依然还是串行处理。原因如下:
  FMDatabaseQueue虽然看似一个队列,实际上它本身并不是,
  它通过内部创建一个 Serial 的 dispatch_queue_t 来处理通过 inDatabase 和 inTransaction 传入的 Blocks.
  所以当我们在主线程(或者后台)调用 inDatabase 或者 inTransaction 时,代码实际上是同步的。
  FMDatabaseQueue这么设计的目的是让我们避免发生并发访问数据库的问题,因为对数据库的访问可能是随机的(在任何时候)、不同线程间(不同的网络回调等)的请求。
  内置一个 Serial 队列后,FMDatabaseQueue 就变成线程安全了,所有的数据库访问都是同步执行,而且这比使用 @synchronized 或 NSLock要高效得多。

  虽然每个queue内部是串行执行的,当时不同的queue之间可以并发执行。

/**
* FMDatabaseQueue如何实现多线程的案例2
*/
- (void)FMDatabaseQueueMutilThreadTest2{
//1、获取数据库文件路径
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileName = [doc stringByAppendingPathComponent:@"students.sqlite"]; //使用queue1
FMDatabaseQueue *queue1 = [FMDatabaseQueue databaseQueueWithPath:fileName]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
[queue1 inDatabase:^(FMDatabase *db) {
for (int i=; i<; i++) {
NSLog(@"queue1---%zi--%@",i,[NSThread currentThread]);
}
}];
}); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
[queue1 inDatabase:^(FMDatabase *db) {
for (int i=; i<; i++) {
NSLog(@"queue1---%zi--%@",i,[NSThread currentThread]);
}
}];
}); //使用queue2
FMDatabaseQueue *queue2 = [FMDatabaseQueue databaseQueueWithPath:fileName]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
[queue2 inDatabase:^(FMDatabase *db) {
for (int i=; i<; i++) {
NSLog(@"queue2---%zi--%@",i,[NSThread currentThread]);
}
}];
}); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
[queue2 inDatabase:^(FMDatabase *db) {
for (int i=; i<; i++) {
NSLog(@"queue2---%zi--%@",i,[NSThread currentThread]);
}
}];
}); //新建多个队列操作同一个 就不发保证线程安全了。不过一般 不会这么用。
}

  

  如果后台在执行大量的更新,而主线程也需要访问数据库,虽然要访问的数据量很少,但是在后台执行完之前,还是会阻塞主线程。 怎么办?

  解决方案:

  1. 如果你是在后台使用的 inDatabase 来执行更新,可以考虑换成 inTransaction,后者比前者更新起来快很多,特别是在更新量比较大的时候(比如更新1000条或10000条)。
  2. 拆解你的更新数据量,如果有300条,可以分10次、每次更新30条。当然有时不能这么做,因为你可能通过网络请求回来的数据,你希望一次性、完整地写入到数据库中,虽然有局限性,不过这确实能很好地减少每个Block占用数据库的时间。
  3. 上面两点可以改善问题,但是问题依然是存在的,在大多数时候,你应该把从主线程调用 inDatabase 和 inTransaction 放在异步里:
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
    [self.databaseQueue inDatabase:^(FMDatabase *db) {
    //do something...
    }];
    });

  

iOS 开发之 FMDB 源码分析的更多相关文章

  1. iOS开发之Alamofire源码深度解析

    今天博客中的Alamofire源码的版本是以现在最新的3.4版本为例.上篇博客系统的对NSURLSession相关的东西进行了详细的解析,详情请看<详解NSURLSession>,为了就是 ...

  2. iOS开发之Alamofire源码解析前奏--NSURLSession全家桶

    今天博客的主题不是Alamofire, 而是iOS网络编程中经常使用的NSURLSession.如果你想看权威的NSURLSession的东西,那么就得去苹果官方的开发中心去看了,虽然是英文的,但是结 ...

  3. iOS开发之Alamofire源码解析

    今天博客中的Alamofire源码的版本是以3.4版本为例.上篇博客系统的对NSURLSession相关的东西进行了详细的解析,详情请看<详解NSURLSession>,为了就是给本篇博客 ...

  4. Qgis插件开发之Qgis源码学习

    Qgis源码中的拖拽.zoomin/out等各个基础功能插件的实现位于qgis_app工程中. 具体头文件为: \QGIS\src\app\qgisapp.h 根据此类可以逐个找到Qgis的基础插件的 ...

  5. 李洪强iOS开发之FMDB线程安全的用法

    // //  ViewController.m //  04 - FMDB线程安全的用法 // //  Created by 李洪强 on 2017/6/6. //  Copyright © 2017 ...

  6. 李洪强iOS开发之-FMDB的用法

    // //  ViewController.m //  04 - FMDB的用法 // //  Created by 李洪强 on 2017/6/6. //  Copyright © 2017年 李洪 ...

  7. 插件开发之360 DroidPlugin源码分析(五)Service预注册占坑

    请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52264977 在了解系统的activity,service,broa ...

  8. 插件开发之360 DroidPlugin源码分析(四)Activity预注册占坑

    请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52258434 在了解系统的activity,service,broa ...

  9. 插件开发之360 DroidPlugin源码分析(三)Binder代理

    转载请注明出处:http://blog.csdn.net/hejjunlin/article/details/52138483 Hook机制中Binder代理类关系图 Hook机制中Binder代理时 ...

随机推荐

  1. 网页格式gbk转utf-8【python requests】

    resp = requests.get(url) resp.content 是str类型 resp.text是unicode类型 如果返回的中文使用gbk编码,需要转换成utf-8的时候: resp. ...

  2. org.apache.httpcomponents.httpclient

    apache org doc :http://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html#d5e49 ...

  3. 题解 SP5271 XOINC - A Coin Game

    SP5271 XOINC - A Coin Game 双倍经验:P2964 [USACO09NOV]硬币的游戏A Coin Game O3做法(TLE):枚举i,j,k,即剩下i枚金币,上一轮选了j枚 ...

  4. redis安装与基本使用

    什么是Redis 什么是NoSQL 介绍Redis之前,先了解下NoSQL (Not noly SQL)不仅仅是SQL 属于非关系型数据库:Redis就属于非关系型数据库 传统的Mysql ,orac ...

  5. 关于Spring+mybatis使用@Transactional注解事物没有生效的问题

    控制台日志信息: was not registered for synchronization because synchronization is not active JDBC Connectio ...

  6. cookie、session以及中间件

    cookie cookie是保存客户端浏览器上的键值对,是服务端设置在客户端浏览器上的键值对,也就意味着浏览器其实可以拒绝服务端的'命令',默认情况下浏览器都是直接让服务端设置键值对 设置cookie ...

  7. Springboot MongoTemplate

    springboot mongodb配置解析 MongoTemplate进行增删改查 mongoTemplate 手把手教springboot访问/操作mongodb(查询.插入.删除) Spring ...

  8. jquery实现登录后右下角弹窗提醒(附带简单样式)

    页面代码如下:   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://w ...

  9. Atcoder Beginner Contest153E(完全背包)

    完全背包,价值取题意代价的最小值 #define HAVE_STRUCT_TIMESPEC #include<bits/stdc++.h> using namespace std; ],b ...

  10. Yii2 TimestampBehavior 用来自动给指定的属性填充当前时间戳

    要使用 TimestampBehavior,把下面的代码加到你的 ActiveRecord 类中: use yii\behaviors\TimestampBehavior; public functi ...