4-4 Core Data–导入大数据集

往 Core Data
应用中导入大数据集是个很普遍的题材。鉴于数据的表征你可以采纳以下二种情势:

从 web 服务器上下载数据 (例如 JSON 数据),然后插入到 Core Data 中。

从 web 服务器上下载预先生成的 Core Data SQLite 数据库文件。

把一个预先生成好的 Core Data SQLite 数据库文件传到应用程序包中。

对某些应用场景后二种采纳作为可行的方案平日被忽视了。因而,在本文中大家将越加的询问她们,并总括一下如何快捷地把web服务上的数码导入到一个动态的应用中。

传输预先生成的 SQLite 文件

当用大批量数额来填充 Core Data 时,通过传输或下载预先生成的 SQLite
文件是一个可行的方案,并且比在客户端创建数量更是便捷。假设源数据库包涵静态数据,并且可以相对独立地与潜在的用户发生的多少共存,这就是该技术的使用处境。

Core Data 框架在 iOS 和 OS X 间是公私的,因而,大家可以创设 OS X
上的命令行工具来发出 SQLite 数据库文件,并且将该文件用在 iOS 应用中。

在大家的事例中
(你可以在Github上找到),我们创制了一个命令行工具,它承受五个柏林(Berlin)城市的数据集文件作为输入,并把它们插入到
Core Data SQLite 数据库中。这一个数据集带有大概 13,000
逗留记录及三百万停留时间记下。

对于该技能最重大的是,命令行工具和客户端应用使用了平等的数据模型。若是数据模型随着时光发出了改动,当您更新应用并传导新的源数据时,你要仔细地管理数据模型的本子。有一个好的提议就是不用复制.xcdatamodel文件,而是从命令行工具项目中把它链接到客户端应用类型。

另一个行之有效的步骤是在发出的 SQLite
文件上推行VACUUM命令。它会减小文件大小,因而根据你传输文件措施的两样,应用程序包的尺寸,或是要下载的数据库的尺码也会相应核减。

除外这个,对于该进度真的没有其余办法了;在大家的案例项目中你也看看了,它就是些简单的正经
Core Data 代码。既然生成 SQLite
文件不是性质关键的天职,你也没须要花大气力去优化它的习性。即使你想让它更快,前边针对火速地导入大数额集到动态应用中所作的总计规则平等适用。

用户暴发的数量

大家日常会有如此的气象,希望有一个可用的大的源数据集,可是也想能积存和改动部分用户暴发的数据。同样,有二种方法来缓解那一个问题。

率先要考虑的是,用户发生的多寡是或不是真正要求用 Core Data
来存储。如若大家能把那些数量存储到 plist 文件中,就绝不乱动已建好的 Core
Data 数据库。

如果大家想用 Core Data
来囤积,另一个索要考虑的问题是,在今天是否需求经过传输更新的先行建好的
SQLite
文件来更新源数据集。假设那种情形不会生出,大家得以高枕无忧地把用户生产的多寡包蕴到同样的数据模型和计划中。可是,假使我们想传输一个新源数据库,大家不可能不要分离源数据与用户暴发的数目。

这些完全可以通过创制第三个精光独立的,使用自己的数据模型的 Core Data
来兑现,或者经过在多少个持久性存储间分发相同有数据模型的数额。对此,大家需求在同一个数据模型中创立第四个配置,它保存用户暴发的数额的实业。当配置
Core Data 栈时,我们将实例化五个持久化存储,其中一个涵盖 URL
和源数据库的配置,另一个包蕴 URL 和用户暴发多少的数据库的配备。

使用三个单身的 Core Data
栈是一种更不难明了的点子。借使这么些办法恰好能缓解您的题目来说,大家强烈推荐使用它。可是,假若你想在用户暴发的数额与源数据间建立关系,Core
Data
不可以帮你达成。即便你把持有的东西包含在一个扩张到七个持久化存储的数据模型中,你依然无法像一般那样在那一个实体间定义关系,可是当得到某一特定属性时,你可以用
Core Data 中的fetched
properties
从差其余蕴藏中自行获取对象。

应用 Bundle 中的 SQLite 文件

假定大家想往应用程序里传输一个预先生成的 SQLite
文件,我们必须检测出最新更新的运用是还是不是是第四遍打开,并把程序外部的数据库文件复制到目的目录:

留神大家首先要删减以前的数据库文件。那不像您想的那样容易明了,因为可能会存在区其余专属文件(如日志或写前几天志文件)与首要的.sqlite文件有关。因而大家无法不遍历目录里的每一项,删除所有的与仓储文件名字匹配不带增添名的文书。

只是,大家也亟需一个主意确保那件事大家只做了五遍。一个很明确的主意就是从程序中把源数据库删除。固然在模拟器上管用,但是因为权限的问题,在真机上会失败。有为数不少方案来解决这几个题目,如在
user defaults 中装置一个 key,它包蕴了前卫导入的数目的版本新闻:

抑或举个例子,大家也足以复制存在的数据库到一个包括源版本的门路来检测它是不是存在,
从而幸免做两个一样的导入。有不少立竿见影的措施供您接纳,那取决你的行使场景最珍贵的是哪些。

下载预先生成的 SQLite 文件

假若由于某些原因大家不想把源数据库包放在应用程序中(如,它会招致程序大小当先手机下载的阈值),大家能够从
web
服务器上下载。进程与我们把数据库文件放在设备上是一模一样的。不过得保险,服务器提供的数据库版本要与客户端的数据模型包容,因为不一致的行使版本数据模型可能会变动。

那不仅是因而下载来替换应用程序中的一个文件,这么些方案也使得填充越来越多的多寡而不造成在客户端动态地导入数据引发的性能与电量损耗成为可能。

为了暴发立时可用的 SQLite 文件,大家可以像后面那么在 (OS X)
服务器上运行类似的命令行导入程序。无可不可以认地,鉴于数据集的大大小小及要服务的伸手数,对每一个呼吁该操作所需的乘除资源可能不允许。一个管用的代表方案是限期地生成SQLite文件,给客户端发送这个现成的文件。

为了提供 SQLite 下载的 API,在劳动器端及客户端当然要求相当的逻辑,SQLite
的下载可以为自上次源文件生成后一度发出变更的客户端提供数据。整个进度有点复杂,不过可以让您更便于的用随机大小的动态数据来填充
Core Data,而且尚未性能问题(除了带宽限制)。

从 Web 服务导入数据

终极,让大家看看如何从 web 服务器上导入大量的多寡,如 JSON 格式的数据。

比方大家要导入有涉嫌的不等目的类型,大家需求在拍卖它们间的关系前先独立地导入所有的靶子。如若我们能在
server
端保证客户端是以正确的依次接受的目的,大家得以立刻处理它们间的关系,而且不要为此担心。但多数情景那是不容许的。

为在不影响用户界面响应前提下进展导入操作,大家无法不在后台线程中执行导入操作。在其次期中,克莉丝(Chris)写了一篇在后台使用
Core
Data
的简便方法。假诺做的不易,多核设备得以在不影响用户界面响应的状态下在后台执行导入操作。注意,并发地使用
Core Data
也有可能在分化的托管对象的前后文间爆发争辩。你需求提出一种策略来预防或处理那一个情形。

在本文中,精晓 Core Data
的产出工作是很要紧的。因为大家早就在多个线程上建立了四个被管理对象上下文,那并不表示它们三个会同时去拜访数据库。从托管对象上下文发出的各类请求会对上下文的对象及
SQLite
文件加上锁。例如,固然你在主上下文的一个子上下文中触发了一个读请求,为了举行这么些请求,主上下文,持久化存储协调器,持久化存储,以及
SQLite 文件都会被加锁(纵然加在SQLite
文件上的锁比其余对象要删减的快
)。在此时期,其余在
Core Data 栈上各类对象会被卡住等着这些请求的形成。

在后台上下文中大量导入数据的例子中,那意味着导入操作的保留请求会不断地在持久化存储协调器上加锁。在此期间,像为了立异用户界面而进行的读取请求,是不可能在主上下文中实施的,而必须等待保存请求完结。因为
Core Data 的 API 是一起的,因而主线程会被卡住,用户界面的响应会受影响。

若是在您的选取场景中那是个问题,你应该考虑为后台上下文使用含有自己的持久化存储协调器的独自
Core Data 栈。在那种情状下,在后台上下文与主上下文间唯一共享的资源就是
SQLite 文件,锁竞争会比从前所有减小。更加地,当 SQLite
文件以write-ahead
loggin
的办法执行
(在 iOS7 和 OS X 10.9 是默许的) 时,固然在 SQLite
文件级别,你也会赢得实在并发。八个读和一个写可以而且来拜会数据库(看那里WWDC
2013 session “What’s New in Core Data and
iCloud”
)

末尾,在大气导入数据时,实时地把修改文告合并到主上下文中一般不会是个好做法。如若用户界面对这几个变迁自动响应的话(通过选用NSFetchResultsController),应用界面会陷入停顿。其实,大家得以在全体导入落成时发送一个自定义公告,让用户界面重新加载数据。

假若接纳场景是想在导入数据里面就实时的更新UI界面,大家可以考虑过滤掉特定实体类型的保留公告,把它们按批聚集起来,或是别的收缩界面更新频率的措施,来保障界面可以响应。但是,在一大半情况下并不值得那样做,因为对界面的累累更新会让用户认为越来越迷惑,而非更有援助。

在经过实际的导入例子讲述了设置方法和操作手段后,我们再来看有的让它尽可能火速的与众不相同方式。

立时地导入

为了疾速导入数据,大家的首先个提出就是通读Apple
关于那个大旨的引导
。大家也会强调该文档中不时容易被忘记的多少个地点。

率先,你要在用来导入的内外文中把undoManager置为nil。即便这么些只适用于 OS
X,因为在 iOS 上,上下文默许没有 undo
manager。把undoManager属性置空会带来首要的属性升高。

说不上,访问具有交互引用论及的对象会暴发引用环。假使您利用了规划精良的电动释放池后,如故看到在导入进程中内存使用持续充实,那就活该小心导入部分代码中的陷阱了。苹果在这里讲述了怎样运用refreshObject:mergeChanges:来去掉这么些环。

当您导入可能早已在数据库中设有的多少时,你须要贯彻部分搜寻及制造的算法,以防患暴发重复。对每一个目的实施读取请求功效很低,因为各类读取请求都急需
Core Data
到硬盘上从存储文件里读取数据。不过,通过按批导入数据并运用在地方提到的文档中
Apple 提供的飞跃查找创立算法,可以很不难幸免那几个问题。

当建立新导入的靶子间的关联时,类似的题材也时不时暴发。用一个读取请求独立地得到每一个相关的靶子是更加低效的。有二种可能的解决办法:一是像按批导入数据那样按批处理它们间的涉嫌,二是缓存已经导入的目的的ID。

按批处理涉嫌足以使大家大大地压缩五遍得到大批量巢倾卵破对象的读取请求次数。不用操心可能很长的查询语句,如:

处理一个在IN
(…)从句中包蕴很多标识符的询问语句,总是比去硬盘上独立地读取每个对象更快速。

然而,也有一种可以完全幸免读取请求的格局,(前提是您只须要在刚导入的目的间建立关联)。要是你缓存导入的兼具目标的
IDs
(实际上在多数动静下数据量也不大),之后你可以用objectWithID:方法为有关的目的建立关联。

瞩目,这些例子借使identifier属性在装有的实体类型中是绝无仅有的,否则,大家就得为大家多缓存的不比档次的对象
IDs 创立重复的标识符。

结论

当你相逢须求导入大批量数量到 Core Data 中时,在做大量 JSON
数据的实时导入前,尽量先不用按正常来思考。尤其是假使您能控制客户端和劳动器端,平时会有众多化解该问题的迅猛方法。可是一旦您只可以忍痛做大批量后台导入工作,保障尽可能与主线程一样独自高效地展开。


翻译小编:李明

网站地图xml地图