概念:

  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. APL: ANSYS Power Library

    1. creating accurate switching current waveforms (profiles) 2.output-state dependent decoupling capa ...

  2. Labview初识

    Labview2013安装教程 请访问http://wenku.baidu.com/link?url=Nw4pYpRqMupd9Bn3OfkFBoYM6Hhw9TqWvffZHX-GDQYPCTtqo ...

  3. 详解多线程MT和多线程MD的区别

    这段时间司在招实习生,而不管是远程的电话面试或者是实际现场面试中领导都喜欢问你这个问题,但是可惜的是能很好答上来的人很少.后来发现不管是应届的实习生,甚至有些实际参加工作几年的人也未必真的了解这个问题 ...

  4. AcWing 847. 图中点的层次

    队列 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm ...

  5. drf三大组件之频率认证组件

    复习 """ 1.认证组件:校验认证字符串,得到request.user 没有认证字符串,直接放回None,游客 有认证字符串,但认证失败抛异常,非法用户 有认证字符串, ...

  6. [linux] 手机Deploy linux 桌面中文乱码

    在手机上安装Deploy之后 通过VNC连接桌面,中文出现乱码 是方块乱码 这个是字体缺失造成的 安装字体就好了 我安装的是kali 桌面是LXDE sudo apt-get install font ...

  7. MySQL必知必会(第4版)整理笔记

    参考书籍: BookName:<SQL必知必会(第4版)> BookName:<Mysql必知必会(第4版)> Author: Ben Forta 说明:本书学习笔记 1.了解 ...

  8. vue 实现todolist,包含添加,删除,统计,清空,隐藏功能

    vue 实现todolist,包含添加,删除,统计,清空,隐藏功能 添加:生成列表结构(v-for+数组).获取用户输入(v-model).通过回车新增数据(v-on+.enter) 删除:点击删除指 ...

  9. Git - Windows 下, gitbash 打开资源管理器

    1. 概述 windows 下 gitbash 打开 资源管理器 2. 场景 资源管理唤起 gitbash 步骤 进入目录 鼠标右击 在 弹出菜单 中, 找到 Git Bash Here 结果 打开一 ...

  10. c# 异常:值不能为 null。 参数名: source

    异常详细信息: System.ArgumentNullException: 值不能为 null.参数名: source 其实问题那就出在 Select() 方法,在 Select 上按 F12 查看定 ...