结识 Realm 的催化剂
在我们公司的项目迭代中,由于在之前的聊天这个模块关于用户信息的传值有问题,而之前因为项目经过很多开发者的手,且不提整体的架构有多混乱,就单说缓存这块,就是乱的不行,有的地方用CoreData,有的地方用FMDB, 而且封装的Manager中方法的声明很乱,存取的逻辑也不是很清晰,于是造成了很多我需要取到数据的时候,根本取不到,而当我修改大部分本次版本迭代的需求时,发现这个取不到值问题如果继续沿用之前的逻辑就会非常的麻烦,我要用很多额外方法去跳过之前的坑,只是我决定,在群组聊天的功能中,将用户数据的传值这块逻辑重构.
传值的逻辑还是比较好重构的,我将原有用在这里的 CoreData代码全部都清掉,让整体功能即使不依赖于缓存,依旧可以正确及时的取到需要的数据,只是需要等待服务器的响应,但是如果每次都去走服务端的网络请求,那么体验就太差了,那么缓存便是很重要的一步.
之前说过,现有项目有,有的地方采用了 CoreData,有的用了 FMDB,十分混乱,而我们的用户量还并涉及不到数据迁移的问题.所以我想采用另一套缓存框架来完成我的需求,那么我第一个想到的就是 Realm.
初识 Realm
Realm是一个跨平台的移动数据库引擎,而且,它是专门为移动端数据应用设计的数据持久化方案.不论是 CoreData,还是传统的SQLite,代码都些许冗余.CoreData的笨拙的API和FMDB相对不那么面向对象的操作方式,可能会很多人望而却步或萌生停用的念头,那么这个时候,Realm 出现了.
Realm既不是 CoreData,也不是SQLite,它拥有自己的数据库存取引擎,它可以跨平台使用,也意味着更加快速的存取速度,官方给出的Realm的存取速度比 CoreData快了3倍,但是据说在实际使用中,当数据量很大时候,Realm的速度比 CoreData 快了不值30倍.
说了这么多,你一定对 Realm 也有了些许好感,这篇文章中我并打算介绍关于 Realm 的使用方法,因为文档Relam的官方文档中写的清清楚楚,网上很多大神也做了相关介绍,但是大多数的博客中方法的介绍都是局限在了 Demo 的使用中,真的作用于项目的很少,我在此也算卖弄卖弄我在植入到实际项目时所遇到的小坑或者经验吧.
1.由于 RLMArray 的关系,这句话一定要写,来定义 RLMArray 中的实例,不然会崩溃
2.由于数据模型已经由继承与 NSObject 的 Model, 改为了继承于 RLMObject,所以在使用 KVC 的时候一定要注意.
3.主键
如果你想要更新数据,主键是不错的选择
4.线程
线程问题的坑是我这篇文章所要说的重点.其实 Realm 在关于线程的处理上已经帮我们做了很多事情,我自己并不需要讲过多的精力放在线程上,但是 Realm 本身的线程管理非常严格,所以我们必须遵守Realm 的使用方式,这就使得坑与有点并存
原本我最开始关于缓存的设计思路是,我从服务端拿到了数据,那么我会把数据放入内存中的数组容器中,再存入数据库中,那么下次进入这个页面我就可以先从数据库中拿到数据后放入容器,再通过服务端进行更新,也是说在当前 Controller 中,我只需要通过数组容器进行赋值就可以了.然而 Realm 并不允许你这样,当我将数据存入 Realm 后,我还没有进行取数据的操作,我只是用数据容器,但这时,程序崩溃了, WTF????!!!! 查看一下崩溃信息 Realm accessed from incorrect thread(从错误的线程访问),当时我就委屈了,我存的时候没有崩溃,也没取啊,怎么就崩溃的,后来我又不得已的用蹩脚的英语水平仔细研读了一下原文文档.
蛋疼的开始
卧槽???看的我是万脸懵逼!!
于是我就交了个 Realm的技术交流群,开始,还有人跟我交流交流使用心德,等把把图贴出来,石沉大海一般,可能是两张截图暴露我的智商和理解能力,大家不想跟差生玩儿...好吧,本着 API 文档就像游戏攻略一样的原则,看不懂的,就带着疑问去玩一玩...那么既然增没事儿,改删查也都没做,那问题会不会出现在了 RLMObject 的调用上,于是在遍历使用之前说的数组容器的地方,打印了当前线程,嗯,果然不是主线程,那既然说同一个 Realm 只要提交了写入就可以在其他线程改变,那我于是试了试 [RLMObject objectWhere:@"查询条件"],发现就完全没有问题的,那原因到底是什么呢?于是我又开始啃文档
这就非常清晰了, Realm 本身在工程中的调用也是个单例类[RLMRealm defaultRealm],所以只要是同一个 Realm, 就可以在任意线程,哪怕是多个线程中,随意使用,不需要锁,只需要将 commitWriteTransaction就可以.但是 RLMObject 的使用的限制就非常严格的,主线程里创建的 RLMObject 就只能在主线程里用,在其他线程中调用的这个它的实例就会抛出异常,有人说,这是 Realm的线程坑,但是我觉得这个是 Realm 对线程做的最好的处理
为此我特意写了一段非常欠打的代码,来验证 Realm 是如何处理并发问题的
、、、
#pragma mark 这是第一个子线程 这里面进行更新写入
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"current ======= %@",[NSThread currentThread]);
for (int i = 0; i < 35; i ++) {
InformationUpdateModel *model = [[InformationUpdateModel alloc] init];
model.name = [NSString stringWithFormat:@"草鞋%d号",i];
model.age = i;
RLMRealm *relam = [RLMRealm defaultRealm];
[relam transactionWithBlock:^{
[relam addOrUpdateObject:model];
[relam commitWriteTransaction];
}];
}
dispatch_async(dispatch_get_main_queue(), ^{
[InfomationModel allObjects];
});
});
、、、
#pragma mark 这是第二个子线程 这里面是查询
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"current ======= %@",[NSThread currentThread]);
[InfomationModel allObjects];
});
#pragma mark 这里是第三个子线程 这里是更新写入加查询 回到主线程后继续更新写入加查询
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"current ======= %@",[NSThread currentThread]);
for (int i = 60; i < 80; i ++) {
InformationUpdateModel *model = [[InformationUpdateModel alloc] init];
model.name = [NSString stringWithFormat:@"草鞋%d号",i];
model.age = i;
RLMRealm *relam = [RLMRealm defaultRealm];
[relam transactionWithBlock:^{
[relam addOrUpdateObject:model];
[relam commitWriteTransaction];
}];
}
[InfomationModel allObjects];
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 40; i < 50; i ++) {
InformationUpdateModel *model = [[InformationUpdateModel alloc] init];
model.name = [NSString stringWithFormat:@"草鞋%d号",i];
model.age = i;
RLMRealm *relam = [RLMRealm defaultRealm];
[relam transactionWithBlock:^{
[relam addOrUpdateObject:model];
[relam commitWriteTransaction];
}];
}
});
});
我直接开了三个子线程,并在其中用同一个 Realm 各自存取
从时间上来看,形成了一个小规模的并发,也是说,会在Realm可能同时即被读,又被写,但是完全没有报出异常,程序流畅运行,数据也没有出现错误.
关于 Realm 就先说到这里,其实 Realm 也有很多不便之处,比如我们的模型必须要继承RLMObject,他的RLMArray也并不是 NSArray类型,所以从代码的独立性角度上来说,就不是特别的完美,如果本身项目中有一套完整严格的数据协议,那么 Realm 可能就不会是一个好的选择,而且本身自带 Model,也被很多架构师的去 Model 化思想想违背,但这并不妨碍它是一套专门用于移动端,速度效率超快, API 非常简洁,线程处理非常棒的持久化解决方案.
后记
第一次写技术文章,诚惶诚恐,其实从代码角度来说,并没有分享什么优质代码,而且废话比较多,可能是因为我个人嘴太碎.这篇文章只是针对自己对于 Realm 的使用做了一些总结,并希望分享出去,这样如果我有理解的不对或者值得讨论的地方,也可以尽快的纠正,当然如果这边文章可以帮到谁,哪怕只有那么一丢丢,我也就心满意足了
其实关于 Realm 的线程处理还有很多更好的方法,如果有机会和时间我会随着业务的深入,再次进行探索,并将心得分享出来共勉.
如果有问题或者不对地方我会及时更正.
作者:Wilson魏
链接:http://www.jianshu.com/p/dd5c7f926218
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Realm的常规使用与线程中的坑的更多相关文章
-
在Java 线程中返回值的用法
http://icgemu.iteye.com/blog/467848 在Java 线程中返回值的用法 博客分类: Java Javathread 有时在执行线程中需要在线程中返回一个值:常规中我们 ...
-
Python并发编程之谈谈线程中的“锁机制”(三)
大家好,并发编程 进入第三篇. 今天我们来讲讲,线程里的锁机制. 本文目录 何为Lock( 锁 )?如何使用Lock( 锁 )?为何要使用锁?可重入锁(RLock)防止死锁的加锁机制饱受争议的GIL( ...
-
解决 spring boot 线程中使用@Autowired注入Bean的方法,报java.lang.NullPointerException异常
问题描述 在开发中,因某些业务逻辑执行时间太长,我们常使用线程来实现.常规服务实现类中,使用 @Autowired 来注入Bean,来调用其中的方法.但如果在线程类中使用@Autowired注入的Be ...
-
在非UI线程中自制Dispatcher
在C#中,Task.Run当然是一个很好的启动新并行任务的机制,但是因为使用这个方法时,每次新的任务都会在一个新的线程中(其实就是线程池中的线程)运行 这样会造成某些情形下现场调度的相对困难,即使我隔 ...
-
线程中调用python win32com
在python的线程中,调用win32com.client.Dispatch 调用windows下基于COM组件的应用接口, 需要在调用win32com.client.Dispatch前,调用pyth ...
-
为什么说invalidate()不能直接在线程中调用
1.为什么说invalidate()不能直接在线程中调用?2.它是怎么违背单线程的?3.Android ui为什么说不是线程安全的?4.android ui操作为什么一定要在UI线程中执行? 1. ...
-
JAVA 线程中的异常捕获
在java多线程程序中,所有线程都不允许抛出未捕获的checked exception(比如sleep时的InterruptedException),也就是说各个线程需要自己把自己的checked e ...
-
【转载】Delphi7从子线程中发送消息到主线程触发事件执行
在对数据库的操作时,有时要用一个子线程来进行后台的数据操作.比如说数据备份,转档什么的.在主窗口还能同是进行其它操作.而有时后台每处理一个数据文件,要向主窗口发送消息,让主窗口实时显示处理进度在窗口上 ...
-
Android的post()方法究竟运行在哪个线程中
Android中我们常用的post()方法大致有两种情况: 1.如果post方法是handler的,则Runnable执行在handler依附线程中,可能是主线程,也可能是其他线程 2.如果post方 ...
随机推荐
-
react.js基本操练
慢慢了解其它的JS前端框架... var data = [{ "when": "2 minutes ago", "who": "J ...
-
Head First 设计模式 --6 命令模式
命令模式:将"请求"封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象.命令模式也支持可撤销的操作.用到的原则:1.封装变化2.组合优于继承3.针对接口编程,不能针对实现 ...
-
[编辑] 分享一些java视频
1.官网:http://www.atguigu.com/,导航栏视频下载,根据自己的需求下载,对应的视频,其次可以下载相应的文档. 2.百度网盘: 链接: http://pan.baidu.com/s ...
-
大规模视觉识别挑战赛ILSVRC2015各团队结果和方法 Large Scale Visual Recognition Challenge 2015
Large Scale Visual Recognition Challenge 2015 (ILSVRC2015) Legend: Yellow background = winner in thi ...
-
mysql建表且某字段内不允许出现重复值
CREATE TABLE `admin` ( `id` ) NOT NULL AUTO_INCREMENT , `username` varchar() NOT NULL , `password` v ...
-
重启Finder
解决Finder卡死的问题! 方法一:在Dock 图标上操作 按住 Option 键并右键点按 Finder 图标,选择菜单中的“重新开启” 方法二:在终端里操作 打开终端(应用程序 – 实用工具), ...
-
JavaScript中的加号
JavaScript中的加号“+”可以作为数学运算符的加,也可以作为字符串拼接,也可以作为一元运算符,表示正数. 1+2 "2"+"3" 3+"2&q ...
-
setprecision、fixed、showpoint的用法总结(经典!!超经典!!)
首先要加头文件:iomanip 一:setprecision 作用:控制输出流显示浮点数的数字个数,setprecision(n)就是输出的n个数,会有四舍五入. 比如:double s=20.784 ...
-
每周.NET前沿技术文章摘要(2017-05-10)
汇总国内外.NET社区相关文章,覆盖.NET ,ASP.NET和Docker容器三个方面的内容: .NET Debugging .NET core with SOS everywhere 链接:htt ...
-
单片机stm32小白入门级学习路线“图”
学习stm32的是真的越来越多了,当然我也是其中语言,所以对于stm32的学习路线非常的感兴趣,所以我也分享一下 虽然是盗图吧 不过也算是分享 ,下边有觉得不错的视频资料 也奉上 (stm ...