二、Realm框架介绍:
realm是一个跨平台移动数据库引擎,支持iOS、OS X(Objective-C和Swift)以及Android;
核心数据引擎C++打造,并不是建立在SQLite之上的ORM, 是拥有独立的数据库存储引擎;
比sqlite, coredata效率更快、简单易用。
三、Realm辅助工具:
1、RealmBrowser:可视化访问Realm数据库(AppStore中可下载)
2、Xcode插件:reaml-cocoa可以快速创建Realm可存储模型对象
(https://github.com/realm/realm-cocoa)
四、Realm支持的数据类型:
BOOL, bool, int, NSInteger, long, long long, float, double, NSString, NSDate, NSData, and NSNumber
注意:不支持集合类型
解决方案:序列化成NSData进行存储 或 转换成RLMArray<RLMObject>进行存储(麻烦)
五、Realm数据库:
1. 用户机制:不同的用户, 使用不同的数据库
+ (void)setDefaultRealmForUser:(NSString *)username {
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
// 使用默认的目录,但是使用用户名来替换默认的文件名
config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent] URLByAppendingPathComponent:username] URLByAppendingPathExtension:@"realm"];
// 将这个配置应用到默认的 Realm 数据库当中
[RLMRealmConfiguration setDefaultConfiguration:config];
}
2. 只读方式打开数据库:config.readOnly = YES;
3. 数据库文件删除:需要删除数据库文件以及辅助文件(官方要求)
NSFileManager *manager = [NSFileManager defaultManager];
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
NSArray<NSURL *> *realmFileURLs = @[config.fileURL,
[config.fileURL URLByAppendingPathExtension:@"lock"],
[config.fileURL URLByAppendingPathExtension:@"log_a"],
[config.fileURL URLByAppendingPathExtension:@"log_b"],
[config.fileURL URLByAppendingPathExtension:@"note"]
];
for (NSURL *URL in realmFileURLs){
NSError *error = nil;
[manager removeItemAtURL:URL error:&error];
if(error){//处理错误}
}
六、Realm增删改查:首先创建数据模型, 必须继承自RLMObject
6.1 创建对象的方式:1. 普通创建
2. 通过父类RLMObject中的方法快速创建:initWithValue(数组或字典)
请注意:跟属性顺序需保持一致;
所有的必需属性都必须在对象添加到Realm前被赋值;
由于Realm在自己的引擎内部有很好的语义解释系统,
所以 Objective‑C 的许多属性特性将被忽略,
如nonatomic, atomic, strong, copy 和 weak 等,
因此为了避免误解,官方推荐在编写数据模型的时候不要使用任何的属性特性。
6.2 简单的数据操作:
1. 保存指定模型:
获取RLMRealm对象:RLMRealm *realm = [RLMRealm defaultRealm];
方式1:开启写入事务:[realm beginWriteTransaction];
添加模型对象:[realm addObject:stu];
提交写入事务:[realm commitWriteTransaction];
方式2:[realm transactionWithBlock:^{
[realm addObject:stu];
}];
方式3:[Stu createInRealm:realm withValue:@{@"stu_id": @22, @"name": @"马冬梅2", @"age": @666}];
2. 更新指定模型
方式1:在事务中直接更新对象
[realm beginWriteTransaction];
stu.name = @"土豆";
[realm commitWriteTransaction];
方式2:根据主键进行更新
1. 要求操作的模型, 必须实现方法
+ (NSString *)primaryKey 返回主键
2. 在事务中调用方法:[realm addOrUpdateObject:stu2];
方式3:根据主键进行更新
1. 要求操作的模型, 必须实现方法
+ (NSString *)primaryKey 返回主键
2. 在事务中调用方法:
[Stu createOrUpdateInRealm:realm withValue:@{@"stu_id": @22, @"name": @"马冬梅2", @"age": @666}];
3. 使用RLMRealm对象, 在事务中删除数据
方式1:删除指定的对象,
注意: 必须是从realm数据库中获取的模型对象, 而不是自己创建的
RLMObject *obj = [realm objectWithClassName:@"Stu" forPrimaryKey:@2];
[realm deleteObject:obj];
方式2:删除所有对象
[realm deleteAllObjects];
4. 使用RLMRealm对象, 查询数据
注意事项:1. 所有的查询(包括查询和属性访问)在Realm中都是延迟加载的,
只有当属性被访问时,才能够读取相应的数据;
2. 查询结果并不是数据的拷贝:
修改查询结果(在写入事务中)会直接修改硬盘上的数据;
3. 一旦检索执行之后, RLMResults将随时保持更新。
查询所有:[Stu allObjects]
条件查询:RLMResults<Stu *> *stus = [Stu objectsWhere:@"name = '马冬梅'"];
排序查询:[stus sortedResultsUsingProperty:@"name" ascending:YES];
链式查询:在查询结果的基础上, 进行二次查询
[stus objectsWhere:@"address beginswith '北京'"];
分页查询:查询出来的结果对象是懒加载, 只有真正访问时,
才会加载相应对象, 所以这里的分页, 其实就是从所有集合中分页获取即可
RLMResults<Dog *> *dogs = [Dog allObjects];
for (NSInteger i = 0; i < 5; i++) {
Dog *dog = dogs[i];
// …
}
七、Realm关系:
7.1 对一关系:当一个对象持有另外一个对象时, 比如人有一个宠物🐶
7.2 对多关系:1. 在Dog中, 遵循指定协议方法
RLM_ARRAY_TYPE(Dog):
RLM_ARRAY_TYPE 宏创建了一个协议,从而允许 RLMArray<Dog> 语法的使用
2. 在Person中, 定义属性:
@property (nonatomic, strong) RLMArray<Dog *><Dog> *dogs;
虽然可以给 RLMArray 属性赋值为 nil,
但是这仅用于“清空”数组,而不是用以移除数组。
这意味着您总是可以向一个 RLMArray 属性中添加对象,
即使其被置为了 nil。
Person属性意义解释:RLMArray: 属性类型。
<Object *>: 属性的特别化(generic specialization),
这可以阻止在编译时使用错误对象类型的数组。
<Object>: 此RLMArray遵守的协议,
可以让 Realm 知晓如何在运行时确定数据模型的结构。
7.3 反向关系:人拥有狗, 狗又有相应的主人?
1. Dog中定义属性:@property (readonly) RLMLinkingObjects *master;
2. 实现协议方法, 标明链接关系:
+ (NSDictionary<NSString *,RLMPropertyDescriptor *> *)linkingObjectsProperties {
return @{
@"master": [RLMPropertyDescriptor descriptorWithClass:NSClassFromString(@"Stu") propertyName:@"dogs"]
};
}
八、可空属性&默认值&忽略属性:
默认情况下, 属性值可空, 如果强制要求某个属性非空, 可以使用如下方法
8.1 设置属性不能为nil:+ (NSArray *)requiredProperties;
特点:如果再次赋值为nil, 则会抛出异常错误
8.2 设置属性默认值:+ (NSDictionary *)defaultPropertyValues;
8.3 设置可忽略属性:+ (NSArray *)ignoredProperties;
开发经验:可以借助忽略属性&只读属性打造计算属性, 完成集合以及UIImage对象的存储与获取
九、Realm数据库通知:
数据库和结果集都可添加通知;Realm实例将会在每次写入事务提交后,
给其他线程上的Realm实例发送通知;必须持有返回的token
9.1 获取Realm通知:
self.token = [realm addNotificationBlock:^(NSString *notification, RLMRealm * realm) {
// 接收到更改通知, 需要做的事情
}];
9.2 移除通知:[self.token stop];
十、Realm数据库迁移:
10.1 数据结构的迁移:
// 在 [AppDelegate didFinishLaunchingWithOptions:] 中进行配置
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
// 设置新的架构版本。
// 这个版本号必须高于之前所用的版本号
//(如果您之前从未设置过架构版本,那么这个版本号设置为 0)
config.schemaVersion = 1;
// 设置闭包,这个闭包将会在打开低于上面所设置版本号的 Realm 数据库的时候被自动调用
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
// 目前我们还未进行数据迁移,因此 oldSchemaVersion == 0
if (oldSchemaVersion < 1) {
// 什么都不要做!
// Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构
}
};
// 告诉 Realm 为默认的 Realm 数据库使用这个新的配置对象
[RLMRealmConfiguration setDefaultConfiguration:config];
// 现在我们已经告诉了 Realm 如何处理架构的变化,打开文件之后将会自动执行迁移
[RLMRealm defaultRealm];
10.2 数据的迁移:
// enumerateObjects:block: 方法遍历了存储在 Realm 文件中的每一个“Person”对象
[migration enumerateObjects:Person.className block:^(RLMObject *oldObject, RLMObject *newObject) {
// 将名字进行合并,存放在 fullName 域中
newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
oldObject[@"firstName"],
oldObject[@"lastName"]];
}];
10.3 属性重命名:
[migration renamePropertyForClass:Person.className oldName:@"yearsSinceBirth" newName:@"age"];
10.4 多版本增量式迁移:根据数据库版本的不同,修改成最新版本(注意不要丢失数据)
// enumerateObjects:block: 遍历了存储在 Realm 文件中的每一个“Person”对象
[migration enumerateObjects:Person.className block:^(RLMObject *oldObject, RLMObject *newObject) {
// 只有当 Realm 数据库的架构版本为 0 的时候,才添加 “fullName” 属性
if (oldSchemaVersion < 1) {
newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
oldObject[@"firstName"],
oldObject[@"lastName"]];
}
// 只有当 Realm 数据库的架构版本为 0 或者 1 的时候,才添加“email”属性
if (oldSchemaVersion < 2) {
newObject[@"email"] = @"";
}
}];
网友评论