概念:

  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. 离线安装requests库

    离线安装需要五个 certifi-2019.9.11-py2.py3-none-any.whl chardet-3.0.4-py2.py3-none-any.whl idna-2.8-py2.py3- ...

  2. python专题知识追寻者对OS的理解

    一 前言 OS(operating system)直接对操作系统进行操作的接口,功能真是非常强大:允许知识追寻者简要概括一下整体模块 如果要对文件进行读写可以使用os.open()方法 如果要对文件路 ...

  3. ThinkPhp5 中Volist标签的用法

    Volist标签一般是和内置方法assign()搭配使用,将值从后台传到前台,是当下比较流行的一种传值方法 本文实例讲述了ThinkPHP模板循环输出Volist标签用法.分享给大家供大家参考,具体如 ...

  4. MyBatis(5)——解决属性名与列名不一致的问题

    解决属性名与列名不一致的问题 问题描述: 当实体类的属性与数据库的列名不对应时取不到该列数据 说明:MyBatis会根据查询的列名设值(列名的setter方法),然后以此列名为做查询等操作,在此过程中 ...

  5. 记录集导出到Excel方法

    记录集导出到Excel方法   Public Function ExportToExcel(RSrecord As ADODB.Recordset, Titles_Name)'============ ...

  6. 8.10-Day1T2圈(circle)

    圈(circle) 题目大意 一开始看这道题,觉得有点像备用钥匙那道题,需要离散化, 把一个球的两个点分开看... 但是..其中的规律我推不出来 (不是很难,只是蒟蒻好久都没有自己独立思考了) 题解 ...

  7. opencv:USM锐化

    USM:unsharp mask 对小的细节干扰小,对大的细节进行锐化 Mat dst; Mat blur_image; GaussianBlur(src, blur_image, Size(3, 3 ...

  8. 吴裕雄 python 机器学习——人工神经网络与原始感知机模型

    import numpy as np from matplotlib import pyplot as plt from mpl_toolkits.mplot3d import Axes3D from ...

  9. mybatis--一对多关联

    今天来介绍mybatis的一对多关联 (1)首先创建数据库mybatisonetomany,并创建数据库表post和user,并向其中插入一定的数据: create database mybatiso ...

  10. KFC 小猪短租

    # 分析肯德基门店信息 import requests,json post_url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op= ...