来源:一缕殇流化隐半边冰霜 (@halfrost )

链接:http://www.jianshu.com/p/d79b2b1bfa72

前言

看了这篇文章的标题,也许有些人还不知道Realm是什么,那么我先简单介绍一下这个新生的数据库。号称是用来替代SQLite 和 Core Data的。Realm有以下优点:

  1. 使用方便

    Realm并不是基于SQLite的对象关系映射数据库。它是使用自己的持久化引擎,为简单和速度而生。用户们说,他们在数分钟之内就上手了Realm,构建一个app只需要数小时,每个app开发时间至少节约数周的时间。

  2. Realm比其他的对象关系映射型数据库(Object Relational Mapping),甚至比原生的SQLite更加快,这都得益于它零拷贝的设计。看看iOS用户和Android用户都是怎么评价它的快的Twitter

  3. 跨平台

    Realm 支持 iOS 和 OS X (Objective‑C & Swift) 和Android。你可以通过使用相同的model,共享Realm文件到各个平台,Java,Swift,Objective-C。并且在全平台可以使用相同的业务逻辑

  4. 优秀的特性

    Realm支持先进的特性,如加密,图形查询,轻松的迁移。Realm的API是一个非常适合打造高响应的应用程序,并且Realm为我们提供方便的组件,以轻松构建复杂的用户界面

  5. 值得信任

    Realm已经获得了银行,医疗保健提供商,复杂的企业app,星巴克这些产品的青睐。

  6. 社区驱动

    Realm是Github上星标最多的数据库里面排名第四,仅次于Java 和 Cocoa 的repos。除了核心工程之外,Realm的社区已经编译了上百个app插件和组件

  7. 支持

    可以从Realm公司快速获得官方的答案,去编译和支持你的数据库。Realm的团队会在Github, StackOverflow, & Twitter回答大家的各种问题

下面再发3张令人惊喜的性能对比图

上图是每秒能在20万条数据中进行查询后count的次数。realm每秒可以进行30.9次查询后count。SQLite仅仅只有每秒13.6次查询后的count,相对于Core Data只有可怜的1。

在20万条中进行一次遍历查询,数据和前面的count相似:Realm一秒可以遍历20万条数据31次,而RCore Data只能进行两次查询。 SQLite也只有14次而已。

这是在一次事务每秒插入数据的对比,Realm每秒可以插入9.4万条记录,在这个比较里纯SQLite的性能最好,每秒可以插入17.8万条记录。然而封装了SQLite的FMDB的成绩大概是Realm的一半,4.7万,Core Data就更低了,只有可怜的1.8万。

从以上3张图可以看出Realm优秀的特性。那么我们开始使用Realm吧。第一步就是把本地的数据库换成Realm。

下面是我翻译的一篇手把手教程,那么让我们赶紧通过教程,来把Core Data迁移到Realm吧。

原文:https://realm.io/news/migrating-from-core-data-to-realm/

译文

把一个使用core data框架作为数据库存储方式的app,迁移到Realm的确是一件很容易的事情。如果你现在有一个已经用了Core Data的app,并且考虑换成Realm,这个手把手教程正适合你!

很多开发者在用户界面,高度集成了Core Data(有时可能有上千行代码),这时很多人会告诉你转换Core Data到Realm可能会花数小时。Core Data和Realm两者都是把你的数据当成Object看待,所以迁移通常是很直接的过程:把你已经存在的Core Data的代码重构成使用Realm API的过程是很简单的。

迁移后,你会为Realm为你app带来的易用性,速度快,和稳定性而感到兴奋。

1.移除Core Data Framework

首先,如果你的app当前正在使用Core Data,你需要找出哪些代码是包含了Core Data的代码。这些代码是需要重构的。幸运的是,这里有一个手动的方式去做这件事:你可以手动的在整个代码里面搜索相关的代码,然后删除每个导入了Core Data头文件声明的语句

#import

//or

@import CoreData;

一旦这样删除以后,每一行使用了Core Data的将会报一个编译错误,接下来,解决这些编译错误只是时间问题。

2.移除Core Data的设置代码

在Core Data中,对model objects的更改是要通过managed object context object来实现的。而managed object context objects又是被persistent store coordinator object创建的,它们两者又是被managed object model object创建的。

可以这么说,在你开始思考用Core Data读取,或者写入数据的时候,你通常需要在你的app中的某处去设置依赖的对象,暴露一些Core Data的方法给你的app逻辑使用。无论在你的application delegate中,全局的单例中,或者就是在inline实现中,这些地方都会存在大量的潜在的Core Data 设置代码。

当你准备转换到Realm时,所有的这些代码都可以删掉。

在Realm中,所有设置都在你第一次创建一个Realm object的时候就已经都完成了。当然也是可以手动去配置它,就像你指定Realm数据文件存储在你的硬盘的哪个路径下,这些完全都可以在runtime的时候去选择的。

RLMRealm *defaultRealm = [RLMRealm defaultRealm];

//or

let realm = Realm()

感觉很好吧?

3.迁移model文件

在Core Data中,实用的那些类都是被定义成NSManagedObject的子类。这些object的接口都是很标准的,原始的类型(比如NSInteger 和 CGFloat)是不能被使用的,它们必须抽象成一个NSNumber对象。

@interface Dog : NSManagedObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, strong) NSNumber *age;

@property (nonatomic, strong) NSDate *birthdate;

@end

@implementation Dog

@dynamic name;

@dynamic age;

@dynamic birthdate;

@end

把这些managed object subclasses转换成Realm是非常简单的:

@interface Dog : RLMObject

@property NSString *uuid;

@property NSString *name;

@property NSInteger age;

@property NSDate *birthdate;

@end

@implementation Dog

+ (NSString *)primaryKey

{

return @"uuid";

}

+ (NSDictionary *)defaultPropertyValues

{

return @{ @"uuid" : [[NSUUID UUID] UUIDString],

@"name" : @"",

@"birthdate" : [NSDate date]};

}

@end

或者

class Dog: Object {

dynamic var uuid = NSUUID().UUIDString

dynamic var name = ""

dynamic var age = 0

dynamic var birthdate = NSDate().date

override static func primaryKey() -> String? {

return "uuid"

}

}

完成!这是多么的简单?

看这些实现,还是有一些Realm的细节需要注意的。

对于初次使用Realm的人来说,没有必要去指定属性关键字,Realm在内部已经管理了。所以这些类的头文件看上去都很精简。此外,Realm支持简单的数据类型,比如NSInteger 和 CGFloat,所有所有的NSNumber都可以安全的删除。

另一方面,这有一些关于Realm model的声明额外的说明。

  1. Core Data objects通过内部的NSManagedObjectID属性去唯一标识一个objects,Realm把这个留给开发者去完成。在上面的例子中,我们额外添加了一个名为uuid的属性,然后通过调用 [RLMObject primaryKey]方法去作为这个class的唯一标识。当然,如果你的objects完全不需要唯一标识,这些都可以跳过。

  2. 在写数据的过程中(这个过程不会太长!),Realm不能处理nil的object的属性。原因是,在[RLMObject defaultPropertyValues]这个类方法中给每个object在最初创建的时候,每个object属性都定义了一系列default值。当然这只是暂时的,我们很高兴的告诉你,在接下来的更新中,我们将会支持Realm object的属性可以为nil。

4.迁移写操作

如果你不能保存你的数据,这肯定不是一个持久的方案!创建一个新的Core Data对象然后再简单的修改一下它,需要下面这些代码:

//Create a new Dog

Dog *newDog = [NSEntityDescription insertNewObjectForEntityForName:@"Dog" inManagedObjectContext:myContext];

newDog.name = @"McGruff";

//Save the new Dog object to disk

NSError *saveError = nil;

[newDog.managedObjectContext save:&saveError];

//Rename the Dog

newDog.name = @"Pluto";

[newDog.managedObjectContext save:&saveError];

相比之下,Realm保存的操作是略有不同的,但在相同的范围内修改上面的代码,仍然有相似的地方。

//Create the dog object

Dog *newDog = [[Dog alloc] init];

newDog.name = @"McGruff";

//Save the new Dog object to disk (Using a block for the transaction)

RLMRealm *defaultRealm = [RLMRealm defaultRealm];

[defaultRealm transactionWithBlock:^{

[defaultRealm addObject:newDog];

}];

//Rename the dog (Using open/close methods for the transaction)

[defaultRealm beginWriteTransaction];

newDog.name = @"Pluto";

[defaultRealm commitWriteTransaction];

或者

//Create the dog object

let mydog = Dog()

myDog.name = "McGruff"

//Save the new Dog object to disk (Using a block for the transaction)

Realm().write {

realm.add(myDog)

}

//Rename the dog (Using open/close methods for the transaction)

Realm().beginWrite()

myDog.name = "Pluto"

Realm().commitWrite()

完成!我们的数据被保存了!

明显的不同是,在Realm中,一旦一个objects被添加到一个Realm object中,它就是不可被修改的。为了在修改属性操作的后面执行,Realm object会被保存在一个写的事务中。这种不能被修改的model,保证了在不同线程中读/写 object数据的情况下,数据的一致性。

Core Data的实现确实可以改变属性,然后调用save方法,对比Realm的实现,只是一些小小的不同罢了。

5.迁移查询

另一方面,如果你不能检索查询你的数据,这肯定不是一个持久的方案!

在Core Data的基础实现中,它运用了fetch requests的概念去从硬盘检索数据。一个fetch request object是被当成一个单独的实例化对象去创建的,包含了一些额外的过滤参数,排序条件。

NSManagedObjectContext *context = self.managedObjectContext;

//A fetch request to get all dogs younger than 5 years old, in alphabetical order

NSEntityDescription *entity = [NSEntityDescription

entityForName:@"Dog" inManagedObjectContext:context];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age

虽然这确实挺好,但是需要编写大量的代码!一些聪明的开发者就开发了一些library使这些代码编写的更加容易。比如MagicalRecord。

对比这些,使用了Realm之后,这些查询的等效代码如下:

RLMResults *dogs = [[Dog objectsWhere:@"age

或者

var dogs = Realm().objects(Dog).filter("age

在一行调用了2个方法。对比Core Data将近10行代码。

当然,相同操作得到的结果是相同的(RLMResults 和 NSArray 基本类似),转换到Realm,由于这些查询都是很独立的,所以查询周围的逻辑只需要重构很少的一部分代码就可以了。

6.迁移用户数据

一旦你所有代码都迁移到Realm,这里还有一个突出的问题,你如何迁移所有用户已经存在在他们设备上的数据,从Core Data迁移到Realm中?

显然,这是非常复杂的问题,它决定于你的app的功能,还有用户的环境。你处理这种情况可能解决办法每次都不一样。

目前,我们看到了2种情况:

  1. 一旦你迁移到Realm,你可以重新导入Core Data framework到你的app,用原生的NSManagedObject objects去fetch你的用户的Core Data数据,然后手动的把数据传给Realm。你可以把这段迁移的代码永久的留在app中,或者也可以经过非常充足的时间之后,再删除掉。

  2. 如果用户数据不是不可替代的——举个例子,如果是一些简单的缓存信息,可以通过硬盘上的用户数据重新生成的话,那么可以很简单的就把Core Data数据直接清除掉,当用户下次打开app的时候,一切从0开始。当然这需要经过非常谨慎的考虑,不然的话,会给很多人留下非常坏的用户体验。

最终,决定应该偏向于用户。理想的情况是不要留下Core Data还连接着你的

app,但是结果还是要取决于你的情况。好运!

进一步的讨论

虽然在移植一个应用程序到Realm过程中,没有真正重要的步骤,但是有一些额外的情况下,你应该知道:

并发

如果你在后台线程做了一些比较重的操作,你可能会发现你需要在线程之间传递Realm object。在Core Data中允许你在线程之间传递managed objects(虽然这样做不是最佳实践),但是在Realm中,在线程中传递objects是严格禁止的,并且任何企图这样做的,都会抛出一个严重的异常。

如此来说,对于下面这些情况,是件很容易的事情:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{

//Rename the dog in a background queue

[[RLMRealm defaultRealm] transactionWithBlock:^{

dog.name = @"Peanut Wigglebutt";

}];

//Print the dog's name on the main queue

NSString *uuid = dog.uuid;

dispatch_async(dispatch_get_main_queue(), ^{

Dog *localDog = [Dog objectForPrimaryKey:uuid];

NSLog(@"Dog's name is %@", localDog.name);

});

});

或者

dispatch_async(queue) {

//Rename the dog in a background queue

Realm().write {

dog.name = "Peanut Wigglebutt"

}

//Print the dog's name on the main queue

let uuid = dog.uuid

dispatch_async(dispatch_get_main_queue()) {

let localDog = Realm().objectForPrimaryKey(Dog, uuid)

println("Dog's name is \\(localDog.name)")

}

}

虽然Realm objects不能在线程间被传递,但是Realm properties的副本可以在线程中被传递。考虑到Realm从磁盘中检索objects是非常快速的,如果只是简单的通过新线程在存储区中重新refetch相同的object,这只会造成很小的性能损失。在这个例子中,我们取了对象的主键的copy,然后把它从后台队列传递给主队列,然后再通过它在主线程的上下文中重新获取该对象。

NSFetchedResultsController 的等效做法

相比Core Data的所有缺点,可能使用Core Data最充足的理由就是NSFetchedResultsController——这是一个类,它可以检测到数据存储的变化,并且能自动的把这一变化展示到UI上。

在写这篇文章的时候,Realm还没有相似的机制。虽然它可以注册一个block,这个block会在数据源发生变化的时候被执行,但是这种”蛮力”的做法对大多数的UI来说都是不友好的。目前,如果你的UI代码很依赖Realm,那么这种做法对你来说就像处理一个breaker一样。

Realm的cocoa工程师现在正在开发一套通知系统,当一些object的属性被更改的时候,允许我们去注册一个通知,来接收到这些改变。这些特性都会在Realm的Swift and Objective‑C 的未来的更新版本中。

在此期间,如果现有的通知block API还是没有满足你的需要,但是你还是需要当特定的property被更改了收到一个通知,这里推荐使用神奇的第三方库,名字叫RBQFetchedResultsController,它能模仿上述功能。除此之外,你还可以通过在objects里面加入setter方法,当setter方法被调用的时候,发送一个广播通知,这样做也能实现相同的功能。

结尾

Core Data和Realm的在展示数据的时候都是通过model objects,由于这一相似性,得以让我们从Core Data迁移到Realm时非常迅速,简单(并且非常令人满意!)。尽管开始看上去令人怯步,但是实际做起来,就是需要把每个Core Data的方法调用转换成等价的Realm的方法,然后写一个辅助类去帮你迁移用户的数据。这些也都非常简单。

如果你在你的app中使用Core Data遇到了些困难,需要些更加简单的解决办法,我们强烈推荐你尝试一下Realm,看看它是否适用于你。如果适用,请你告诉我们!

感谢阅读这篇文章。快去用Realm构建一个令人惊喜的app吧!在这些地方可以联系到我们

StackOverflow

https://stackoverflow.com/questions/tagged/realm?sort=newest

GitHub

https://github.com/realm/realm-cocoa

Twitter

https://twitter.com/realm

手把手教你从Core Data迁移到Realm相关推荐

  1. Core Data 迁移与版本管理

    原文  http://chun.tips/blog/2014/11/28/core-data-ban-ben-qian-yi-jing-yan-zong-jie/ 主题 Core Data iOS开发 ...

  2. Core Data 迁移

    此文摘抄自:http://objccn.io/issue-4-7/ 自定义 Core Data 迁移似乎是一个不太起眼的话题.苹果在这方面只提供了很少的文档,若是初次涉足此方面内容,很可能会变成一个可 ...

  3. 手把手教你微信公众号迁移_四步教你怎么迁移微信公众号

    教你迁移微信公众号_迁移完成后,原账号就注销了,无法再登陆公众号后台,登进去是这样的界面 微信公众号迁移说难不难,说简单也不简单.下面用4步来教会你们操作微信公众号迁移,每一点都写的非常详细,记得收藏 ...

  4. win10 uwp 手把手教你使用 asp dotnet core 做 cs 程序

    本文是一个非常简单的博客,让大家知道如何使用 asp dot net core 做后台,使用 UWP 或 WPF 等做前台. 本文因为没有什么业务,也不想做管理系统,所以看到起来是很简单. Visua ...

  5. 云架构师进阶【SAP迁移方案设计】手把手教您玩转SAP上云

    一,项目背景介绍 今天又接到了北京某制造企业SAP迁移的场景,先简单说明一下,好好的SAP在运行着为什么要做迁移这类大动作呢?众所周知SAP的ERP是企业的绝对核心系统,尤其对于制造业更是核心的系统, ...

  6. iOS Core Data 数据迁移 指南 144 作者 一缕殇流化隐半边冰霜 关注 2016.05.09 00:35* 字数 4718 阅读 2931评论 17喜欢 327 前言 Core

    iOS Core Data 数据迁移 指南 作者 一缕殇流化隐半边冰霜 关注 2016.05.09 00:35* 字数 4718 阅读 2931评论 17喜欢 327 前言 Core Data是iOS ...

  7. 官方资源帖!手把手教你在TensorFlow 2.0中实现CycleGAN,推特上百赞

    铜灵 发自 凹非寺 量子位 出品| 公众号 QbitAI CycleGAN,一个可以将一张图像的特征迁移到另一张图像的酷算法,此前可以完成马变斑马.冬天变夏天.苹果变桔子等一颗赛艇的效果. 这行被顶会 ...

  8. 手把手教创建你的第一个以太智能合约:ETHEREUM PET SHOP(译)

    手把手教创建你的第一个以太智能合约:ETHEREUM PET SHOP(译) 原文地址 : http://truffleframework.com/tutorials/pet-shop 译者:luci ...

  9. 手把手教你搭建Hadoop生态系统伪分布式集群

    Hello,我是 Alex 007,一个热爱计算机编程和硬件设计的小白,为啥是007呢?因为叫 Alex 的人太多了,再加上每天007的生活,Alex 007就诞生了. 手把手教你搭建Hadoop生态 ...

最新文章

  1. 使用PLP特征训练crnn语音分类
  2. tf.nn.embedding_lookup函数的用法
  3. (数据结构与算法)数组和单链表实现栈的基本操作
  4. LSM 自适应信号处理代码
  5. java中的JDBC用户管理系统_Java+JDBC学生信息管理系统
  6. PHP对Excel导入导出操作
  7. 在线自动下载最新版本jquery
  8. [css] css常用的布局方式有哪些?
  9. 75-100-024-测试-MySQL 双表优化案例
  10. 要想完全放弃Windows使用Linux需要多少勇气?
  11. 2021年南宁二中高考成绩查询,2021年广西南宁二中高考物理冲刺试卷(一).docx...
  12. 归并排序(包含逆序数对的个数51Nod1019)
  13. 【指纹识别】基于MATLAB/FPGA的指纹识别算法仿真实现
  14. 基于MATLAB的柱状图画法及特殊柱状图画法总结
  15. LimeSDR常见问题及解决方法
  16. JavaScript防流量劫持-前端安全
  17. python 关于sys.argv[1]语句报错IndexError: list index out of range的解决
  18. 计算机exo乐谱,【图片】【送谱】EXO所有歌曲的谱子【吉他谱吧】_百度贴吧
  19. win10 + ubuntu双系统及20系显卡驱动安装
  20. Lio_sam运行测试环节遇到的问题以及实测总结

热门文章

  1. Java实现1到n的倒数的累加和
  2. 报告解读丨细数万亿企服市场发展史,揭秘行业发展新趋势
  3. 金融实战篇:最佳数据驱动之城商行
  4. 中奖名单 | 价值 199 的大数据平台研发课程幸运er
  5. 听说蒂姆·库克来了?这和我们有什么关系!
  6. Object构造函数的方法
  7. 解决Intellij IDEA部署JavaWeb项目 404问题
  8. mysql utc 下取得昨天的时间段。
  9. 探讨.NET Core数据进行3DES加密和解密问题
  10. 在winform嵌入外部应用程序