sqlite4-5 Core Data–Fetch 请求

将对象从存储中取出来的主意之一是运用NSFetchRequest。但是请留心,一个最普遍的荒谬是在您不须求的时候去读取数据。请保管您已经阅读并掌握了取得对象一节中的内容。一大半时候,遍历关系进一步有效,而选取NSFetchRequest往往开支很高。

普普通通有四个原因使用NSFetchRequest来执行多少获得:(1) 你须求为匹配特定谓词
(predicate) 的目的搜索整个对象图;或者 (2) 你想要在诸如 table view
那样的地点显得所有的目的。其实还有第两种,也是一个较不常见的气象,就是在遍历关系的同时却想要更敏捷地预先获取数据。大家也将简单长远这些题目。可是大家先来探视四个首要缘由,它们更是普遍并且每个都持有自己的扑朔迷离。

基础

在那里大家不会涉及基础内容,因为一个有关 Core Data 的名为Fetching
Managed
Objects

Xcode 文档已经包含了多量基本原理。大家将长远到有些更标准的上边。

搜索对象图

在我们的直通数据的事例中,我们有
12,800 个车站,其中有像样 3,000,000 个停留时间相互关联。对类似北纬 52°
29′ 57.30″,东经 +13° 25′ 5.40″ 的车站,若是大家想要根据发车时间介于
8:00 和 8:30 之间的尺度来展开查找,大家不会想要在这些 context
中加载所有的 12,800
个车站对象和富有三百万的停留时间对象,然后再对它们进行巡回访问。若是大家这么做,将只可以开销多量岁月以及相当大的存储空间以将有所的靶子加载到存储器中。取而代之,大家想要的是接纳SQLite 来压缩进入内存的的目的的数量。

让大家从小处初阶,为地方接近北纬 52° 29′ 57.30″ 东经 +13° 25′ 5.40″
的车站创制一个 fetch 请求。首先大家成立这几个 fetch 请求:

咱俩接纳Florian 的 data model
文章中
中关系的+entityName方法。然后,大家须求将结果限定为那个看似大家的点的结果。

俺们可以概括的用一个 (不完全)
正方形区域环绕大家的兴趣点。实际在数学上那多少复杂,因为地球恰好有点类似于一个椭球。不过借使大家只要地球是球体,则可以赢得这一个公式:

咱俩最后可以拿走以下内容 (均为近似值):

俺们的感兴趣的点是:

CLLocation *pointOfInterest = [[CLLocation alloc]
initWithLatitude:52.4992490

longitude:13.4181670];

大家想在 ±263 英尺(80 米)内开展搜寻:

(当大家好像 180° 经线的时候,这些运算不创设。由于我们的通畅数据出自离
180° 经线很远很远的德国首都,所以大家忽视这一个题材。)

点名一种排序描述符毫无意义,因为大家会在内存中做第二次遍历。不过大家将让
Core Data 在回到对象里填上享有值。

假如不做这么些设置,Core Data 将把值取入持久化存储协调器的行缓存 (row
cache)
中,而不是填充实际目的。通常来说那是没问题的,然而由于大家将立即访问具有目标,所以大家并不期望出现那种行为。

编者注:把属性值先取入缓存中,在目的急需的时候再开展几回访问,这在 Core
Data 中是默许行为,那种技术称为
Faulting。这么做可以防止下落内存费用,不过如果你规定将访问结果对象的现实属性值时,可以禁用
Faults 以增长获取性能。

为平安防护考虑,最好增加:

推行那条 fetch 请求

获取操作败北唯一 (可能) 的原委是储存器损坏(文件被删去等等),否则就是
fetch 请求中出现了语法错误。所以在此处运用NSAssert()是平安的。

咱俩现在利用 Core Locations,对内存中的数额做第二次遍历。

和:

迄今甘休大家成功了总体装置。

地理定位性能

选用装载了 SSD 硬盘的新一代 MacBook Pro 读取这个数量平均约需求360µs,也就是说,你每秒能够做大概 2800 次请求。OPPO 5 平均约要求1.67ms,每秒 600 次请求。

若是加上-com.apple.CoreData.SQLDebug1作为启动参数传递给应用程序,我们将得到如下输出:

除开部分 (对于仓储本身的) 计算音讯外,实际为读取数据而生成的 SQL 是:

那多亏大家所梦想的。假若我们想要对这一个特性举办调查研商,大家能够使用
SQLEXPLAIN命令。为此,大家得以像上边那样使用命令行sqlite3来开辟数据库:

那告诉大家 SQLite 为(ZLONGITUDE>? AND ZLONGITUDEmodel
作品中描述的那么选择复合索引则会做的更好。由于我们总是同时搜寻经度和纬度的重组,这么做会更便捷,而且我们得以去掉经度和纬度各自的目录。

那将使输出像上边这样:

在大家的简便案例中,加上复合索引大概不影响属性。

就像在SQLite
文档
中的表达一(Wissu)样,假使你的出口里带有SCAN
TABLE的话,你就要进步警惕了,那基本上意味着 SQLite
须求遍历所有的笔录来看看那些是相匹配的。除非你只存储了很少的几个对象,否则你都应该利用应用
index。

子查询

一旦大家只想要这几个看似大家的且在接下去 20 分钟之内提供服务的车站。

咱俩得以像这么为StopTimes的实业创造一个谓词:

不过只要大家想要的谓词是足以依据与Stop提姆es
停留时间
对象的关系而过滤出这个Stop
车站
对象,而不是停留时间目标自我的话,大家得以行使一个如此的子查询:

请小心,假若类似傍晚,这几个逻辑是稍有缺点的,因为大家应当将谓词一分为二。可是该逻辑在这么些例子中是卓有成效的。

对于限制数量在论及之上的,子查询至极有效。在 Xcode 文档-[NSExpression
expressionForSubquery:usingIteratorVariable:predicate:]
中有更加多音信。

咱俩得以简单的采纳and或者&&来组合多少个谓词,例如:

仍然在代码中动用+[NSCompoundPredicate
andPredicateWithSubpredicates:]

大家用一个像那样的谓词来作为完毕:

子查询性能

一经大家看一下转变的 SQL,它会像上面那样:

这几个 fetch 请求在新一代 MacBook Pro 上运行大概要求 12.3 ms。在 黑莓 5
上,大致必要 110 ms。请小心,我们有 300 万 个停留时间 和接近 13,000
个车站。

explan 那么些查询,结果如下:

请小心,大家怎样对谓词排序卓殊重大。我们期待把经纬度放在前方,因为代价低,而子查询由于代价高则放在语句最终。

文件搜索

搜寻文本是一种普遍的气象。在大家的事例中,来探视使用名称来寻觅车站实体。

柏林(Berlin)有个被称为 “U Görlitzer Bahnhof (Berlin)”
的车站。一种很傻很天真的检索该站的法门如下:

假如您想规行矩步如下所示做的话,事情会变得更糟
(比如进行一项大小写和(或)音调不灵敏的询问。):

其实,事情并不是那么粗略。Unicode
万分复杂,并且有不少骗局。主要的是众多字符可以因此多种措施来表示。U+00F6U+006F都代表U+0308都得以象征
“ö.”。倘若你身处 ASCII 码的世界之外时,像大写 /
小写这么的概念就会非凡复杂。

SQLite
会为您减轻负担,但它是要付出代价的。固然它看起来很直接,但事实并非如此。对于字符串搜索,我们想做的是在大家有一个规范化的本子可以在里面进展查找。咱们将解除音调符号,把字符串变成小写字母,然后将其放入一个normalizedName字段中。然后大家将对用于搜索的字符串做同样的业务。然后
SQLite
就无须考虑音调和大小写,在尺寸写和声调不灵动的气象下,搜索就仍会很快。然则大家必须先形成一多元繁重的义务。

在新一代 MacBook Pro
上,使用示例代码应用BEGINSWITH[cd]和示范的字符串搜索要求 7.6ms (130
次搜索 / 秒),在 索爱 5 上这么些数字是每回搜索 47ms,每秒举办 21
次搜索。

为了将字符串转换为小写并移除其音调,我们可以利用CFStringTransform():

大家将更新Stop类来自动更新normalizedName:

有了那些,大家就足以用BEGINSWITH代替BEGINSWITH[cd]来查找了:

在新一代 MacBook Pro 上,使用示例代码中的示例字符串搜索BEGINSWITH需要6.2ms(160 次搜索 / 秒),在 金立 5 差不离上急需 40ms,25 次搜索 /
秒。

擅自文本搜索

咱俩的物色还不得不在字符串的始发和寻找字符串相匹配的意况下有效。要化解这一个题目就要创制另一个用来搜寻的实体。大家称这一个实体为SearchTerm,给其一个normalizedWord属性,以及一个和Stop的关系。对于每个车站大家将业内它们的名目,并将其拆分成一个个词。例如:

对此每个词。大家成立一个SearchTerm和一个从Stop到它的保有SearchTerm对象的关联。当用户输入一个字符串,大家用来下代码在SearchTerm对象的normalizedWord上搜索:

那也能够在Stop对象中直接用子查询达成。

得到具有目的

一经我们的拿走请求中尚无设置谓词,我们将为获取到给定实体的享有目的。尽管我们对Stop提姆es实体那样做的话,大家将会牵涉
300
万个对象。那将会变得慢性,以及占用多量内存。可是有时,大家就是索要获得具有目标。常见的例证是大家想要在一个
table view 中彰显所有指标。

在这种情况中,我们要做的是设置批处理量:

request.fetchBatchSize = 50;

当大家设置了批处理量运行-[NSManagedObjectContext
executeFetchRequest:error:]的时候,大家照样会博得一个回去的数组。大家可以查询它的因素数量(对于Stop提姆(Tim)es实体而言,那将类似
300 万),然而 Core Data
将只会随着大家对数组的巡回访问将目的填充进去。倘使那一个目标不再被访问,Core
Data 则会另行清理对象。简单的讲,数组的批处理量为
50(在那么些事例中)。Core Data 将一遍得到 50
个对象。一旦有超过一定数额的批量目的,Core Data
将释放最旧一批目的。于是,你就足以在如此的数组中循环访问具有目的,而无需在存储器中并且存具备
300 万个目的。

在 iOS 中,要是你选用NSFetchedResultsController且有不胜枚举目标,请确保您的
fetch
请求中装置了fetchBatchSize。你需求实际试验以确定多少的处理量更切合你。一般的话,将其安装为您要显示的多寡的两倍,会是一个没错的起来。


翻译小编:树叶

网站地图xml地图