一、Realm 框架
官网地址
介绍:
(1)、realm 是一个跨库平台的移动端数据库引擎,支持 iOS、OS X (objective-C和swift) 以及android
(2)、核心数据库引擎C++ 打造,并不是建立在sqlite上的ORM,是拥有独立的数据库引擎.
(3)、性能,据说比sqlite 和coredata要快
(4)、易用性,相比sqlite、coredata,使用起来更加简单,更易入门.
二、使用教程
三、辅助工具
-
1、Realm Browser --> 可是化的访问Realm 数据库的工具,直接在 app store 搜索realm 即可下载.
- 2、Xcode 插件 下载后直接运行即可,成功后打开Xcode 可看见如下图标,即成功了.
四、Xcode导入realm 框架
- 创建podfile 安装realm
platform :ios, '9.0'
target 'realm Demo' do
use_frameworks!
pod 'Realm', '~> 3.1.1'
end
五、Realm 实战
一、简单的数据操作
- 1、准备:
(1) 、创建的数据模型(objective-c 类)必须是继承自RLMObject的对象.
(2)、由于realm 在自己的引擎内部有很好的语义解释系统,所以objective-c 的许多属性特性将被忽略,如: nonatomic, atomic, strong,copy,和weak等.因此为了避免误解,官方推荐在编写数据模型时不要使用任何的属性特性.
(3)、所有的必须属性(排除忽略的)在对象添加到realm前,都必须被赋值.
(4)、 创建对象的方式,可以通过普通方式创建, 也可以使用 RLMObject 中的方法快速创建initWithValue
(5) 注意:Dog *dog =[ [Dog alloc]initWithValue:@{@"name":@"xiaohuang",@"age":@(22)}];
在realm 中只能存储,BOOL, bool,int, NSInteger,long, long long ,float ,double,NSString,NSData,NSDate,NSNumber ,像NSArray NSDictionary 是不能存储的.
- 2、 使用RLMObject 对象,保存指定的模型:
(1) 、方式1:
(2) 、方式二://1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它) RLMRealm *realm = [RLMRealm defaultRealm]; //2. 创建 数据模型对象 Dog *dog = [[Dog alloc]initWithValue:@{@"name":@"xiaoHuang", @"age":@(2)}]; //3. 开启写入事物 [realm beginWriteTransaction]; //4. 添加数据模型 [realm addObject:dog]; //5. 提交写入事物 [realm commitWriteTransaction]; // 注意: 对realm 数据库的增 删 查 改 的操作都必须写在realm 的事物中来执行,否则报错
(3)、方式三//1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它) RLMRealm *realm = [RLMRealm defaultRealm]; //2. 创建 数据模型对象 Dog *dog = [[Dog alloc]initWithValue:@{@"name":@"xiaoHuang", @"age":@(2)}]; // 3. [realm transactionWithBlock:^{ //4. 添加数据模型 [realm addObject:dog]; }];
注意://1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它) RLMRealm *realm = [RLMRealm defaultRealm]; //2. bolck 事物 [realm transactionWithBlock:^{ //3. 添加数据模型 [Dog createInRealm:realm withValue:@{@"name":@"xiaoHuang", @"age":@(2)}]; }];
在realm 的事物中没有回滚的概念,如果执行时出错,它内部会自动回滚,外面想要监听操作的异常,就使用下面这个方法,如果错处就会有 errorNSError *err = nil; [realm transactionWithBlock:^{ //3.增 删 查 改 }error:&err]; if(err){ NSLog(@"realm 在 操作过程中出现了错误: %@",err); }
-
3 使用RLMRealm 对象, 更新指定的模型
(1) 、方式1: 在事物中直接更新对象.
注意:
此种方式更新的对象必须是已经和realm 关联的对象,否则无效,如下:[realm transactionWithBlock:^{ //4.注意 在realm 的事物中要 更新的 模型数据必须是 和 realm 已经建立关系的模型数据 // 否则没办法执行 dog.age = 20; }];
(2) 、方式2: 更具主键去更新(必须要告诉主键)
[realm transactionWithBlock:^{ // 注意: 通过这种方式 来添加或者更新某个数据, 模型的类必须实现 +(NSString *)primaryKey; 方法 [realm addOrUpdateObject:dog]; }];
(3) 、方式3:
[realm transactionWithBlock:^{ // 注意: 通过这种方式 来添加或者更新某个数据, 模型的类必须实现 +(NSString *)primaryKey; 方法 [Dog createOrUpdateInRealm:realm withValue:@{@"name":@"xiaohei",@"age":@(2)} ]; }];
-
4、 使用RLMRealm 对象 ,删除数据
(1)方式1: 删除指定对象[realm transactionWithBlock:^{ // 注意: 要删除的对象必须是和real 相关联的对象, 自定义的没有和realm 关联的对象是不能被删除的 [realm deleteObject:dog]; }]; // 也就是说,如果你要删除某个制定个的数据对象, //要先从realm 中把它查找出来,让后在删除.
(2)方式2: 删除所有的对象(有坑)
// 注意: 这个方法有点猛, 他会删除你所有表的所有的内容, 因此要慎用 [realm transactionWithBlock:^{ [realm deleteAllObjects]; }]; // 如果只想删除某一张表,或一类数据,那么要分2步. // 第一步: 通过realm 查出某一张表的所有的对象 RLMResults *rsts = [Dog allObjects]; // 这是一个支持快速遍历的集合 // 第二不: 删除查询出来的所有的数据 [realm transactionWithBlock:^{ [realm deleteObjects:rsts]; }];
-
5、使用RLMRealm 对象查询数据(有坑)
(1) 、注意事项:
(1.1) 所有的查询(包括查询和属性访问)在realm 中都是延迟加载的,只有当属性被访问时,才能够读取响应的数据
(1.2) 查询结果并不是数据的拷贝:修改查询结果(在写入事物中)会直接修改硬盘删的数据.
(1.3)一旦检索执行后,RLMResults 将随时保持更新.RLMRealm *realm = [RLMRealm defaultRealm]; // 查询所有的数据 RLMResults *rsts = [Dog allObjects]; NSLog(@"前=========rstsCount:%ld, rsts : %@",rsts.count, rsts); // 查询完成后再插入一条数据 [realm transactionWithBlock:^{ [realm addObject:[[Dog alloc]initWithValue:@{@"name":@"xiaoHei",@"age":@(2)}]]; }]; NSLog(@"后=========rstsCount:%ld, rsts : %@",rsts.count, rsts); // rsts 的数据信息会动态的变化而不是固定的, 前后两次打印信息是不同的
(2)、查询所有
// 所有继承自 RLMObject 的对象都有这个方法 [Dog allObjects];
(3)、条件查询
// 查询条件和 sql 语法一样 RLMResults<Dog *> dogs = [Dog objectsWhere:@"age < 3"];
(4)、排序
[dogs sortedResultsUsingProperty:@"age" ascending:YES];
(5)、链式查询:
含义:
在查询结果的基础上,进行二次查询[dogs objectsWhere:@"address begingswith '成都' "];
(6)分页查询 (没有具体的分页,需要自己查出结果集,自己取想要的)
二、 Realm 支持的数据类型
- 1 、Realm 只支持 BOOL ,bool, NSinteger,long, long long,float,double,NSString, NSDate,NSDate,NSNumber 这几种.
- 2 、注意: 不支持集合(NSArray,NSDictionary)类型, 但是可以支持RLMArray 这种集合类型
// RLMArray 是这样定义的 @interface RLMArray<RLMObjectType> : NSObject<RLMCollection, NSFastEnumeration> // 可以看出, RLMArray 内存储的对象必须是继承自 RLMObject 的对象, RLMArray 相当于Foundation 框架中的NSArray //在Person 数据模型中使用集合 //第一步: 定义Dog 数据类型 @interface Dog : RLMObject @property NSString *name; @property(nonatomic, assign)int age; @end //这一行比不可少 RLM_ARRAY_TYPE(Dog) //第二步: 定义Person 数据类型 @interface Person : RLMObject @property NSString *name; @property int age; //1. 通过以下这种方式在模型数据中 定义 realm集合 //2. 指定集合中存储的是 Dog 类型 @property RLMArray<Dog *><Dog> *dogs; @end
三 、realm 数据之间的关系
- 1 、对一关系:
当一个对象持有另一个对象是,这种关系我们就称之为对一
关系,比如: 一个person 对象
持有一个Dog 对象
.//Person.h 文件 #import <Realm/Realm.h> #import "Dog.h" @interface Person : RLMObject @property NSInteger num; @property NSString *name; @property int age; /** realm 中的一对一的关系 */ @property Dog *dog; @end RLM_ARRAY_TYPE(Person) // Person.m 文件 #import "Person.h" @implementation Person +(NSString *)primaryKey{ return @"num"; } @end //Dog.h 文件 #import <Realm/Realm.h> @interface Dog : RLMObject @property NSInteger num; @property NSString *name; @property int age; @end RLM_ARRAY_TYPE(Dog) // Dog.m 文件 #import "Dog.h" @implementation Dog +(NSString *)primaryKey{ return @"num"; } @end // 实际方法应用 - (void)testExample { //1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它) RLMRealm *realm = [RLMRealm defaultRealm]; Person *pson = [[Person alloc] initWithValue:@{@"num":@(1), @"name":@"zhangsan",@"age":@(18)}]; Dog *dog = [[Dog alloc]initWithValue:@{@"num":@(1), @"name":@"xiaoHei",@"age":@(1)}]; pson.dog = dog; // 3. [realm transactionWithBlock:^{ //4. 添加数据模型 [realm addObject:pson]; }]; NSLog(@"pson: %@",pson); }
- 2、 对多关系:
(1)在Dog
类中,遵循指定协议方法,RLM_ARRAY_TYPE(Dog)
,RLM_ARRAY_TYPE
宏创建了一个协议,从而允许RLMArray<Dog* ><Dog> * dogs;
这种语法的使用.
(2)在Person
类中,定义属性@property RLMArray<Dog* ><Dog>* dogs;
注意:
(2.1)、虽然可以个RLMArray
属性赋值为nil
,但是这仅用于清空
数组,而不是移除数组,这意味着你总是可以向一个RLMArray
属性中添加对象,即使其被值为了nil
//Person.h 文件 #import <Realm/Realm.h> #import "Dog.h" @interface Person : RLMObject @property NSInteger num; @property NSString *name; @property int age; /** realm 中的一对多的关系 */ @property RLMArray<Dog *><Dog> *dogs; @end RLM_ARRAY_TYPE(Person) // Person.m 文件 #import "Person.h" @implementation Person +(NSString *)primaryKey{ return @"num"; } @end //Dog.h 文件 #import <Realm/Realm.h> @interface Dog : RLMObject @property NSInteger num; @property NSString *name; @property int age; @end RLM_ARRAY_TYPE(Dog) // Dog.m 文件 #import "Dog.h" @implementation Dog +(NSString *)primaryKey{ return @"num"; } @end // 实际方法应用 - (void)testExample { //1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它) RLMRealm *realm = [RLMRealm defaultRealm]; Person *pson = [[Person alloc] initWithValue:@{@"num":@(1), @"name":@"zhangsan",@"age":@(18)}]; Dog *dog1 = [[Dog alloc]initWithValue:@{@"num":@(1), @"name":@"xiaoHei",@"age":@(1)}]; Dog *dog2 = [[Dog alloc]initWithValue:@{@"num":@(2), @"name":@"xiaoHuang",@"age":@(2)}]; // person 的dogs 属性是一个RLMArray, 在使用时不不会要创建realm 内部会自动创建 // 只需要将 属性 添加进去即可 [pson.dogs addObject:dog1]; [pson.dogs addObject:dog2]; // 3. [realm transactionWithBlock:^{ //4. 添加数据模型 [realm addObject:pson]; }]; NSLog(@"pson: %@",pson); }
- 3 、反向关系:
比如: Person 拥有Dog,Dog 又有相应的主人.//Person.h 文件 #import <Realm/Realm.h> #import "Dog.h" @interface Person : RLMObject @property NSInteger num; @property NSString *name; @property int age; /** realm 中的一对多的关系 */ @property Dog *dog; @end RLM_ARRAY_TYPE(Person) // Person.m 文件 #import "Person.h" @implementation Person +(NSString *)primaryKey{ return @"num"; } @end //Dog.h 文件 #import <Realm/Realm.h> @interface Dog : RLMObject @property NSInteger num; @property NSString *name; @property int age; //1. 在realm 中有个特性,凡是 readonly 的属性,在生成数据库表时,该字段会自动被忽略 //2. 在realm 中如果需要定义反向属性,就按下面这个格式定义一个属性即可,名字自己取 //3. 不需要指定反向对象的类型, 只需要在.h文件说明反向属性的 名称,如下: @property(readonly)RLMLinkingObjects *master; @end RLM_ARRAY_TYPE(Dog) // Dog.m 文件 #import "Dog.h" @implementation Dog #pragma mark- 反向映射关系描述 // 此方法用户描述 反向属性 的映射关系 +(NSDictionary<NSString *, RLMPropertyDescriptor*> *)linkingObjectsProperties{ // 描述: name: 反向映射关系的属性名 // value, RLMPropertyDescriptor 描述器, 用于说明反向属性是哪个类, 中的哪个属性 return @{@"master":[RLMPropertyDescriptor descriptorWithClass:NSClassFromString(@"Person") propertyName:@"dog"]}; } #pragma mark- 主键 +(NSString *)primaryKey{ return @"num"; } @end - (void)testExample { //1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它) RLMRealm *realm = [RLMRealm defaultRealm]; Person *pson = [[Person alloc] initWithValue:@{@"num":@(1), @"name":@"zhangsan",@"age":@(18)}]; Dog *dog = [[Dog alloc]initWithValue:@{@"num":@(1), @"name":@"xiaoHei",@"age":@(1)}]; pson.dog = dog; // 3. [realm transactionWithBlock:^{ //4. 添加数据模型 [realm addObject:pson]; }]; NSLog(@"pson: %@",pson); }
四、可空属性&默认值&忽略属性
- 1、默认情况下,属性值是可空的,如果强制要求某个属性非空,可以使用下面的方法.
(1)、遵循协议方法,如下:+(NSArray *)requiredProperties{ return @[@"name1",@"name2"]; } // 如果遵守了些协议方法说明了字段不能为nil , //那么给对应的属性设置为nil 时后操作数据库就会报警告.
-
2、默认值 (遵守协议)
+(NSDictionary *)defaultPropertyValues{ return @{@"name":@"value"}; }
-
3、忽略属性(某些不想存入数据库的字段)
(1)方式1 、遵守协议+(NSArray *)ignoredProperties{ @[@"name", @"age"]; }
(2) 方式2 、也可使用只读属性,即
// 只读属性,在realm 中是被忽略的. // 建议使用这种方式,直观 @property(readonly)NSString *address;
五、通知
1、realm 实例会在每次写入事物提交后,给其他线程上的realm实例发送通知.
-
2、 realm 通知的具体用法(方式1)
@property (nonatomic, strong) RLMNotificationToken * token; // realm 的通知必须强引用,否则收不到通知 self.token = [realm addNotificationBlock:^(RLMNotification notification, RLMRealm *realm) { // 接收到通知后的执行逻辑 }]; // 移除通知 [self.token stop];
注意:
(1)创建通知的token 是需要强引用的,否则通知是不生效的.
(2)移除通知时要使用对应的token 3、 realm 通知的具体用法(方式2)
六、realm 数据库操作
- 1 realm 的数据库是采用用户机制来区分数据的,不同的用户使用不同的数据库,以达到数据分离的目的
// 封装方法,更具用户名指定realm数据库 -(void)setDefaultRealmForUsr:(NSString *)usrName{ RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration]; // 使用默认的存储目录,但是使用用户名来替换默认的文件名 NSURL *configFileUrl = [config.fileURL URLByDeletingLastPathComponent]; configFileUrl = [configFileUrl URLByAppendingPathComponent:usrName]; configFileUrl = [configFileUrl URLByAppendingPathExtension:@"realm"]; // 将这个配置应用到默认的 realm 数据当中去 [RLMRealmConfiguration setDefaultConfiguration:config]; } // 测试 - (void)testExample { // 设置默认的数据库 [self setDefaultRealmForUsr:@"zhangsan"]; //1. 获取realm 对象 (可以理解成为数据库的句柄, 对数据库的增删查改都要用到它) RLMRealm *realm = [RLMRealm defaultRealm]; Person *pson = [[Person alloc]initWithValue:@{@"name":@"张三",@"age":@(14)}]; [realm transactionWithBlock:^{ [realm addObject:pson]; }]; }
七 数据库文件删除
- 注意:
需要删除数据库文件以及辅助文件-(void)deleteRealm{ NSFileManager *fileMgr = [NSFileManager defaultManager]; RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration]; NSArray<NSURL *> *fileUrls = @[config.fileURL, [config.fileURL URLByAppendingPathExtension:@"lock"], [config.fileURL URLByAppendingPathExtension:@"log_a"], [config.fileURL URLByAppendingPathExtension:@"log_b"], [config.fileURL URLByAppendingPathExtension:@"note"], ]; for (NSURL *url in fileUrls) { NSError *err = nil; [fileMgr removeItemAtURL:url error:&err]; if (err) { NSLog(@"删除 文件:%@ 失败",url); } } }
八、realm 数据迁移
数据迁移适用于修改了模型的情况下
- 1 数据结构迁移
- 2 数据迁移
- 3 属性重命名
- 4 多版本增量式迁移
七、realm使用总结说明:
(1)、realm 底层不是基于sqlite 数据库实现的.
(2)、realm底层是采用C++来实现的.
(3)、凡是在realm 的写事物中指定的删 改 对象
代码必须是和realm中有映射关系的.