iOS 开发之 FMDB 源码分析
概念:
FMDB 是用于数据存储的框架,它是 iOS 平台下对 SQLite 数据库的封装。FMDB 是面向对象的,它以 OC 的方式封装了 SQLite 的 C 语言 API,使用起来更加方便。
Core Data是 ORM(对象关系映射) 的一种体现,使用Core Data需要用到模型数据的转化,虽然操作简单,不需要直接操作数据库,但是性能没有直接使用SQLite高。但是SQLite使用的时候需要使用c语言中的函数,操作比较麻烦,因此需要对它进行封装。但是如果只是简单地封装,很可能会忽略很多重要的细节,比如如何处理并发以及安全性更问题。
使用第三方框架FMDB,它是对libsqlite3框架的封装,用起来的步骤与SQLite使用类似,并且它对于多线程的同时操作一个表格时进行了处理,也就意味着它是线程安全的。FMDB是轻量级的框架,使用灵活,它是很多企业开发的首选。
重要的类:
FMResultSet : 表示FMDatabase执行查询之后的结果集。
FMDatabase : 表示一个单独的SQLite数据库操作实例,用来执行SQL语句, 通过它可以对数据库进行增删改查等等操作。
FMDatabaseAdditions : 扩展FMDatabase类,新增对查询结果只返回单个值的方法进行简化,对表、列是否存在,版本号,校验SQL等等功能。
FMDatabaseQueue : 使用串行队列 ,对多线程的操作进行了支持,用于在多线程中执行多个查询或更新,它是线程安全的。
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]);
}
}];
}); //新建多个队列操作同一个 就不发保证线程安全了。不过一般 不会这么用。
}
如果后台在执行大量的更新,而主线程也需要访问数据库,虽然要访问的数据量很少,但是在后台执行完之前,还是会阻塞主线程。 怎么办?
解决方案:
- 如果你是在后台使用的
inDatabase来执行更新,可以考虑换成inTransaction,后者比前者更新起来快很多,特别是在更新量比较大的时候(比如更新1000条或10000条)。 - 拆解你的更新数据量,如果有300条,可以分10次、每次更新30条。当然有时不能这么做,因为你可能通过网络请求回来的数据,你希望一次性、完整地写入到数据库中,虽然有局限性,不过这确实能很好地减少每个Block占用数据库的时间。
- 上面两点可以改善问题,但是问题依然是存在的,在大多数时候,你应该把从主线程调用
inDatabase和inTransaction放在异步里:dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
[self.databaseQueue inDatabase:^(FMDatabase *db) {
//do something...
}];
});
iOS 开发之 FMDB 源码分析的更多相关文章
- iOS开发之Alamofire源码深度解析
今天博客中的Alamofire源码的版本是以现在最新的3.4版本为例.上篇博客系统的对NSURLSession相关的东西进行了详细的解析,详情请看<详解NSURLSession>,为了就是 ...
- iOS开发之Alamofire源码解析前奏--NSURLSession全家桶
今天博客的主题不是Alamofire, 而是iOS网络编程中经常使用的NSURLSession.如果你想看权威的NSURLSession的东西,那么就得去苹果官方的开发中心去看了,虽然是英文的,但是结 ...
- iOS开发之Alamofire源码解析
今天博客中的Alamofire源码的版本是以3.4版本为例.上篇博客系统的对NSURLSession相关的东西进行了详细的解析,详情请看<详解NSURLSession>,为了就是给本篇博客 ...
- Qgis插件开发之Qgis源码学习
Qgis源码中的拖拽.zoomin/out等各个基础功能插件的实现位于qgis_app工程中. 具体头文件为: \QGIS\src\app\qgisapp.h 根据此类可以逐个找到Qgis的基础插件的 ...
- 李洪强iOS开发之FMDB线程安全的用法
// // ViewController.m // 04 - FMDB线程安全的用法 // // Created by 李洪强 on 2017/6/6. // Copyright © 2017 ...
- 李洪强iOS开发之-FMDB的用法
// // ViewController.m // 04 - FMDB的用法 // // Created by 李洪强 on 2017/6/6. // Copyright © 2017年 李洪 ...
- 插件开发之360 DroidPlugin源码分析(五)Service预注册占坑
请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52264977 在了解系统的activity,service,broa ...
- 插件开发之360 DroidPlugin源码分析(四)Activity预注册占坑
请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52258434 在了解系统的activity,service,broa ...
- 插件开发之360 DroidPlugin源码分析(三)Binder代理
转载请注明出处:http://blog.csdn.net/hejjunlin/article/details/52138483 Hook机制中Binder代理类关系图 Hook机制中Binder代理时 ...
随机推荐
- (matlab)自定义图像(matlab)
clc;clear all;A=[0 230 255 60 30 100];A=uint8(A);imshow(A,'InitialMagnification','fit') 如图: clc;clea ...
- A task in a suit and a tie:paraphrase generation with semantic augmentation解读
1.该算法核心:在seq2seq模型的编码器中增加语义的frame 和 roles 2.上图为算法整个流程: 1).首先输入一句话s,SLING会使用frame和role label注释输入语句s,然 ...
- NUMPY的学习之路(2)——索引,合并,分割,赋值
一.索引 1.1numpy数组的转置 A=np.arange(3,15).reshape(3,4) print(A) print(A[2][0]) print(A[2,1]) print(A[2,:] ...
- 505,display,float,position之间的关系(有疑问)
(display属性设置元素如何显示) 如果display取值为none,那么position和float都不起作用,这种情况下元素不产生框 否则,如果position设置框是绝对定位,float的计 ...
- Go_栈
1. 栈的介绍 2. 栈的应用 3. 栈入门 package main import ( "fmt" "errors" ) //使用数组来模拟一个栈的使用 ty ...
- IEC 60958 && IEC 61937
IEC 60958 IEC 60958是一种传递数字音频的接口规范,相比I2S,IEC60958通过一根线同时传递时钟信号和数据信号.IEC 60958用来传递两channel,16/20/24bit ...
- (原创)Windows下编译的Shell脚本不能再Linux中运行的解决办法
一.原理 Windows编译的文件和Linux编译的文件格式不太一样,导致在Linux运行Shell脚本的时候会提示:/bin/bash^M: bad interpreter: 没有那个文件或目录. ...
- [C/C++] 只允许程序运行一个实例
原理是创建一个内核对象之后 如果再创建一个同名的对象 就会给代码中的GetLastError函数对应的变量修改为 ERROR_ALREADY_EXISTS (但是不影响"创建"对象 ...
- 深入理解Java虚拟机(1)
Java内存区域 对于Java程序员来说,在虚拟机的自动内存管理机制下,不再需要为每一个new操作去写配对的delete和free代码,不容易出现内存泄露和内存溢出问题,可以直接交给虚拟机进行管理. ...
- Jmeter_JsonPath 提取器
1.登录老黄历 2.提取阳历的数据,不用正则表达式提取器,因为这里是字典形式,用Json path提取器更简单 3.把提取的数据放到百度里去发送请求 4. 5. 6. 7. 8. 9.