Realm
- Realm 是由美国YCombinator孵化的创业团队历时几年打造,第一个专门针对移动平台设计的数据库
- Realm 是一个跨平台的移动数据库引擎,目前支持 iOS、Android平台,同时支持 Objective-C、Swift、Java、React Native、Xamarin 等多种编程语言
- Realm 并不是对 SQLite 或者 CoreData 的简单封装, 是由核心数据引擎 C++ 打造,是拥有独立的数据库存储引擎,可以方便、高效的完成数据库的各种操作
Realm 的类定义说明
在Realm框架中,定义了二十个核心类、常量、枚举类型、协议等,常用的如:RLMRealm类、RLMObject类、RLMResults类等, 我们可以从Realm的官方网站上查看所有的定义以及使用说明
RLMRealm 类
- 一个 RLMRealm 类的对象可以认为是一个 Realm 的数据库。Realm 数据库既可以存储在硬盘上,同时也可以存储在内存中
- Realm 是框架的核心所在,是我们构建数据库的访问点,就如同 Core Data 的管理对象上下文 managed object context 一样
- RLMRealm 类中,常用的属性或方法如下
1 | // 获取默认的Realm数据库 |
RLMObject类
- 在Realm数据库中存储的都是RMObject对象,RLMObject类是所有可以存储在Realm数据库中的对象的根类
- 凡是可以存储在Realm数据库中的对象都是RLMObject类或RLMObject类的子类
- 要创建一个数据模型,我们只需要继承RLMObject,然后设计我们想要存储的属性即可
- 在RLMObject类中,我们可以添加属性,添加的属性类型可以支持如下类型:
- NSString:字符串
- NSInteger, int, long, float, double:数字型,注意没有CGFloat
- BOOL/bool:布尔型
- NSDate:日期型
- NSData:二进制字符型
- NSNumber
: 其中X必须RLMInt, RLMFloat, RLMDouble或 RLMBool类型 - RLMArray
: 其中X必须是RLMObject类的子类, 用于建模多对多关系 - RLMObject的子类,用于建模多对一关系
- RLMObject类中,比较常用如下方法:
1 | //创建Realm对象, 传入一个NSArray或NSDictionary实例来设置对象属性的值 |
RLMResults 类
- 当我们执行一个查询操作后,查询出满足条件的RLMObject对象会存放在一个RLMResults对象中
- RLMResults类是一个数组类型的数据结构,因此在其类定义中,提供了很多与数组类似的属性和方法
- 相关属性与方法
1 | //结果集合中的对象个数 |
Realm 的使用
创建RLMObject类
我们首先创建一个Student类,该类是RLMObject类的一个子类, 下图就是按照之前安装的Xcode插件创建的
- 在Student添加两个属性, RLMObject官方建议在RLMObject类中添加的属性,是不需要指定属性关键字的,完全交由Realm处理
- 假如设置了,这些attributes会一直生效直到RLMObject被写入realm数据库
- RLM_ARRAY_TYPE宏创建了一个协议,从而允许 RLMArray
语法的使用 - 如果RLM_ARRAY_TYPE宏没有放置在模型接口的底部的话,您或许需要提前声明该模型类
1 | @interface Student : RLMObject |
存储操作
对于RLMObject类型的对象,我们可以直接对创建的对象进行存储
- 初始化对象
1 | // 方式一: 接受一个数组对象 |
- 把RLMObject对象写入Realm数据库, 同样有三种方式
1 | //方式一: 提交事务处理 |
注意
- 所有的必需属性都必须在对象添加到Realm前被赋值
如果在进程中存在多个写入操作的话,那么单个写入操作将会阻塞其余的写入操作,并且还会锁定该操作所在的当前线程
- 建议常规的最佳做法:将写入操作转移到一个独立的线程中执行
- 官方给出的建议:由于Realm采用了MVCC设计架构,读取操作并不会因为写入事务正在进行而受到影响
- 除非您需要立即使用多个线程来同时执行写入操作,不然您应当采用批量化的写入事务,而不是采用多次少量的写入事务
下面的代码就是把写事务放到子线程中去处理
1
2
3
4
5
6
7dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject: stu4];
}];
});
查询操作
- Realm中也提供了功能强大的数据查询能力,如果会使用SQL语言的话,上手的难度更低
- 在Realm的查询功能中,也可以像SQL一样使用各种条件查询关键字,查询的结果会保存在一个RLMResults类的数组中
- 全量查询, 通过调用allObjects方法, 得到该表中的所有数据
- 条件查询,设置一些查询条件,从而查询出符合条件的对象
- Realm的查询条件可以使用==、<=、<、>=、>、!=、BETWEEN、CONTAINS 以及 ENDSWITH等多种操作符
全量查询
1 | //1. 获取所有数据 |
条件查询
1 | //条件查询 |
更新操作
- 需要修改的模型一定是被Realm所管理的模型, 而且已经和磁盘上的对象进行地址映射
- 对新添加的模型进行更新
1 | //获取Realm对象 |
根据查询到的数据更新指定属性的数据
1 | //条件查询 |
- 当有主键的情况下, 使用Update方法
- addOrUpdateObject会去先查找有没有传入的Student相同的主键,如果有,就更新该条数据
- 这里需要注意,addOrUpdateObject这个方法不是增量更新,所有的值都必须有,如果有哪几个值是null,那么就会覆盖原来已经有的值,这样就会出现数据丢失的问题
- createOrUpdateInRealm:withValue这个方法是增量更新的,后面传一个字典,使用这个方法的前提是有主键
- 方法会先去主键里面找有没有字典里面传入的主键的记录,如果有,就只更新字典里面的子集;如果没有,就新建一条记录
1 | //获取Realm对象 |
删除操作
- 删除的模型, 一定要求是被realm所管理的已经存在的模型
- 当需要在Realm中删除某些对象时,需要注意的是,该方法的执行需要在一个事务中进行
1 | //获取Realm对象 |
Realm数据库机制
- 上面用到的获取realm对象的方式都是通过defaultRealm来获取默认配置的realm对象
- 当我们需要创建不同的realm表格时又该如何操作呢?
- 下面我们来看一下
1 | - (void)setDefaultRealmForUser:(NSString *)username { |
做好上述配置之后, 便可创建不同的数据库了
1 | // 不同的用户, 使用不同的数据库 |
通知
- Realm实例将会在每次写入事务提交后,给其他线程上的Realm实例发送通知
- 一般控制器如果想一直持有这个通知,就需要申请一个属性, 强引用该属性,strong持有这个通知
- 集合通知是异步触发的,首先它会在初始结果出现的时候触发,随后当某个写入事务改变了集合中的所有或者某个对象的时候,通知都会再次触发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27//强引用属性
@property (nonatomic, strong) RLMNotificationToken *token;
- (void)setUp {
[super setUp];
RLMRealm *realm = [RLMRealm defaultRealm];
// 获取 Realm 通知
self.token = [realm addNotificationBlock:^(RLMNotification _Nonnull notification, RLMRealm * _Nonnull realm) {
NSLog(@"接收到变更通知--%@", notification);
}];
//结束该通知
[self.token stop];
}
- (void)testExample {
NoticeModel *noticeM = [[NoticeModel alloc] initWithValue:@{@"num": @1, @"name": @"sz"}];
//添加数据, 数据操作之后便会通知上述通知中心执行相应操作
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject:noticeM];
}];
}
数据库迁移
- 数据库存储方面的增删改查应该都没有什么大问题,比较蛋疼的应该就是数据迁移了
- 在版本迭代过程中,很可能会发生表的新增,删除,或者表结构的变化,如果新版本中不做数据迁移,用户升级到新版,很可能就直接crash了
- 数据迁移一直是困扰各类型数据库的一大问题, 但是对于Realm来说, 却方便很多, 这也是Realm的优点之一
- 新增删除表,Realm不需要做迁移
- 新增删除字段,Realm不需要做迁移; Realm会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构
1 | //需要在以下方法中进行配置 |