前言
下面是我对realm研究的第一篇文章的传送门:初窥Realm数据库。这篇文章主要是对之前一篇的补充,会对realm做更深一步的探讨。我会根据realm的一些特性展开介绍。
事务
realm对数据的操作都是基于事务的。首先介绍一下什么是事务:事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。我认为最重要的属性是原子性,这保证了线程安全,同时在一个事务中的诸多操作,只要一个不成功,其他皆不成功。这样可以尽力保证我们的数据是干净的。
realm提供了两种构建事务的方法:
[realm beginWriteTransaction];
//你的操作
[realm addObject:province];
[realm commitWriteTransaction];
基于block的构建方法:
[[RLMRealm defaultRealm] transactionWithBlock:^{
//你的操作
[[RLMRealm defaultRealm] addObject:province];
}];
这两种方式的效果是一模一样的,也就是说block构建方式也是同步的,点进去看一下源码:
- (BOOL)transactionWithBlock:(void(^)(void))block error:(NSError **)outError {
[self beginWriteTransaction];
block();
if (_realm->is_in_transaction()) {
return [self commitWriteTransaction:outError];
}
return YES;
}
这样看出block只是起到了一个代码块的作用。这个在官网文档中也有提及。
realm的正确用法
从刚才的源码看出,每次你开启一个事务,realm会做出两件事,一个是开启写入的权限,然后提交你的事务。一个事务的处理需要的开销其实是比较大的。一些连续的操作应该尽量放在一个事务中去处理。
错误的用法:
for(int i=0;i<array.count;i++)
{
ProvinceEntity *province = [pDic objectForKey:array[i]];
[[RLMRealm defaultRealm] transactionWithBlock:^{
[[RLMRealm defaultRealm] addObject:province];
}];
}
正确的用法:
[[RLMRealm defaultRealm] transactionWithBlock:^{
for(int i=0;i<array.count;i++)
{
ProvinceEntity *province = [pDic objectForKey:array[i]];
[[RLMRealm defaultRealm] addObject:province];
}
}];
RLMResults自动更新:
RLMResults是底层数据的动态表现,其会进行自动更新,这意味着检索到的结果不能进行重复检索。它们会反映出当前线程上 Realm 的当前状态,包括在当前线程上的写事务当中:
RLMResults<Dog *> *puppies = [Dog objectsInRealm:realm where:@"age < 2"];
puppies.count; // => 0
[realm transactionWithBlock:^{ [Dog createInRealm:realm withValue:@{@"name": @"大黄", @"age": @1}];}];
puppies.count; // => 1
根据这个特性,realm提供了对数据监听的一些接口,这样数据一旦发生变化,就能进行ui的更新,非常的方便。这也是realm的强大之处。
realm数据库迁移
对于移动端数据库来说最头疼的就是数据库版本的迁移了。一张表中字段的新增,更新,删除,都会影响上个版本的用户,如果不做迁移,升级以后就直接crash了。回想一下coredata的迁移,假如你新增了表或者是新增了字段,那就要做新的版本的去迁移。步骤也是很麻烦的,还要重新生成文件。如果你表中字段发生变化了,那就麻烦了,要做map。
realm的数据库容错性还是不错的:
- 1.新增删除表,不需要做迁移
- 2.新增删除字段不需要做迁移。Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构。
realm比较厉害的一点是,他不光能map字段,还能根据你的需要去匹配。接下来是例子:
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 2;
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion)
{
// 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"] = @"";
}
}];
};
[RLMRealmConfiguration setDefaultConfiguration:config];
// 现在我们已经成功更新了架构版本并且提供了迁移闭包,打开旧有的 Realm 数据库会自动执行此数据迁移,然后成功进行访问
[RLMRealm defaultRealm];
总结
realm日常需要用到的方法和一些注意点差不多就这些了,掌握这些对于日常的开发不成问题。这也体现出了realm学习成本并不高,后面我会封装一些比较常用的方法出来,放在github上供大家使用。如果你对realm有什么疑问或者见解,欢迎给我留言。
下面是我封装的一个helper,主要是对更新和删除进行了封装,使用起来更加的简单传送门:RealmHelper
网友评论