开展白
您好,欢迎来到WWDC。大家好。我是CoreData小组的Rishi Verma。在本次会议中,我们将向您展示如何利用CoreData最适合应用程序的需求。首先,我们将研究如何通过批处理操作快速有效地填充和维护持久性存储。然后,我们将讨论如何定制提取任务以匹配应用程序的需求。最后是一些提示和技巧。应用程序如何对持久性存储中的更改做出反应。
快速有效地填充和维护持久性存储。
让我们先看一下样本:地震。它是一个Swift应用程序,具有用于驱动UI的视图上下文和用于提取美国地质调查局提供的数据的背景上下文。我们的示例具有一个本地应用容器,并从USGS收集地震数据作为JSON提要。
在这里,我们将JSON feed发送到我们的JSON解析器。依次将数据发送到我们的后台上下文,以将其转换为地震管理的对象,并对我们的本地存储安全。然后,我们的视图上下文合并更改以神奇地更新我们的UI。现在,我们的背景上下文可以处理大量创建或获取的托管对象,这些托管对象仅在保存后不久就被丢弃。但这就是批处理操作的亮点。批处理操作使开发人员可以轻松地进行插入,更新和删除,同时尽可能地少。
由于这些操作的极简主义性质,因此请注意以下几点。 没有发布保存通知,因为很显然我们不在这里进行保存。由于我们没有显露托管对象,所以我们没有获得任何回调或访问器逻辑来进行更改。但是,请耐心等待,您可以持久地解决这两个警告。启用持久性历史记录并捕获您的批处理操作,以便可以轻松获取通知,现在我们已经解决了批处理操作的第一个警告。至于回调和访问器逻辑,我们可以通过分析持久性历史记录来适应应用程序当前视图的相关更改,以适应这种情况。我们如何更深入地进行批处理操作呢?我们的应用程序通常要做的第一件事是将数据加载到持久性存储中,从而驱动我们的UI。
当我们介绍NSBatchInsertRequest时,它为开发人员提供了批处理操作以处理数据的能力。它简化了提取大量数据的能力。现在,我们扩展了NSBatchInsertRequest的功能。最初,我们使开发人员能够为批量插入传递一系列字典。词典数组表示要创建的对象,键为属性名称及其分配的值。我们还有一个新功能,一个初始化程序,允许开发人员给出一个块来填写给定的字典或托管对象。这极大地减少了摄取的峰值内存,同时还进一步减少了对象分配的数量。我们如何看一个例子呢?在这段代码中,我们正在为地震创建托管对象。并使用USGS提供的地震数据值填充它们。然后我们保存。让我们看看如果采用NSBatchInsertRequest的话该代码的外观。首先,我们收集所有地震数据的字典并将其添加到数组中。我们创建一个批处理插入请求并执行,很简单。
现在,让我们看一下批量插入请求的这种块变体。我们使用一个块创建批处理插入请求,该块将为给定字典分配值。
然后,我们执行请求,并调用该块,直到它返回“ true”作为停止和保存的指示符。但是这三种不同的方式表现如何?让我们看一下在摄取大量地震时应用程序的性能。当我们使用托管对象保存上下文时,我们看到它花费了超过一分钟的时间,并且闲置了大约30兆的内存。最初的高峰是JSON数据,而第一分钟的大部分时间实际上不是摄取,而是变更通知的合并。由于我们进行了大量交易。但是批量插入没有问题。
当我们使用带有字典数组的NSBatchInsert时,我们用25兆的空闲内存完成了相同的操作,并且能够在13秒钟内保存相同数量的对象,这是传统保存所需时间的一小部分。但是我们可以做得更好。让我们使用新的块摄取,我们能够在11秒内摄取所有对象。
批处理插入的巧妙技巧
现在,我们已经优化了数据填充,让我们学习有关批处理插入的巧妙技巧。在这里,我们从地震样本中获得了管理对象模型,并选择了地震实体。右侧是地震实体的数据模型检查器。让我们仔细看看。我们可以看到属性代码是唯一的约束。这意味着持久存储中只有一个对象可以具有特定的代码值。没有其他地震具有相同的代码值。这对我们的地震样本有何影响?好了,我们的JSON feed提供了过去30天的所有地震。并且,每当用户单击重新加载按钮时,我们都会从JSON feed中获取所有数据,这些数据几乎是相同的,除了最近发生的几次地震和过去地震的更新数据。
在这里,我们发生了地震,代码42来自我们的JSON提要进入解析器。然后将其传递到我们的背景环境中,从而将地震42保存到商店中。第一次提取它时,我们在商店中创建了一个新行。但是,在随后尝试插入同一地震的过程中,我们不想删除旧地震并插入新地震。我们真的很想更新任何已更改的数据,在SQL中,这称为Upsert。 Upsert是一个SQL术语,如果您同时看到SQL,则更容易理解。因此,这里我们插入了地震对象,如果在代码上有冲突,则在插入时,请更新这些属性,而不要插入。您如何获得这种行为?只需在执行对NSMergeByPropertyObjectTrumpMergePolicy的批处理插入请求的上下文中设置合并策略。但是有一种更简单的更新方法,而批量更新再简单不过了。使用NSBatchUpdateRequest,无需执行获取任务即可仅更新托管对象并保存。 NSBatchUpdateRequest可以快速有效地更新满足Fetch请求中定义的搜索条件的对象中的属性。让我们来看一个简单的例子。使用我们的地震样本应用程序。如果我们能够通过其他来源进行确认,我们可以将所有地震标记为已验证。但是,让我们想象一下,如果震级大于2.5,我们的来源只会验证地震。好吧,我们可以为此进行批量更新。这段代码为地震创建了一个批处理更新请求,然后将属性设置为“ updated”(等于)“ true”,并设置谓词(大于2.5)。然后我们执行批处理更新,我们都完成了。就这么简单。因此,我们已经介绍了插入和更新。让我们进行批量删除。
批量删除功能非常强大,可用于轻松删除对象图的大部分。遵守关系规则。因此删除将级联,并且关系将被取消。我们看到的一般用例来自到期代码,该代码确定对象的生存时间并清除已到期的对象。这是此API的绝佳用法。但是,看似简单的操作可能会产生复杂的后果。一个例子呢?这里有一个例子,地震数据在30天后过期。由于这是一个清理任务,因此我们以后台优先级异步调度它。我们的块从托管对象上下文开始,并确定到期日期。
然后,我们构建获取请求并设置到期谓词。
使用我们的新提取请求创建批处理删除请求并执行。嗯但是,如果我们有大量符合批量删除搜索条件的对象怎么办?该请求将对我们的商店进行正确锁定,时间可能是无限制的。但是,等等,我们可以解决此问题。让我们设置提取限制。这样,我们的任务就不会无止境。而且,我们已经使我们的用户摆脱了很多挫败感。现在我们已经避免了这种陷阱,让我们看看如何改善获取数据的方式。现在我们有了这个丰富的对象图,我们需要调查并显示商店中的商品。在获取数据时,我们返回的数据可以驱动许多视图和计算。但是我们是否总是需要那么多数据。又如何在没有整个对象图的情况下进行这些复杂的计算呢?首先,managedObjectResultType提供了最轻松地遍历对象图的最简单方法。当我们使用结果对批处理结果控制器进行批处理时,这非常好,因为我们的托管对象已更新,我们的提取结果控制器神奇地响应并应用了差异。让我们看看实际情况。这是我们的地震样本,没有任何数据。然后我们获取。提取对象时,提取结果控制器将添加行。当我们更新这些对象时,我们的视图也会更新。但是,如果您在这里注意到,我们的视图仅显示大约15次地震。我们获得的不只是这些。我们在这里还有一些改进的空间。在我们的提取任务中说出批量大小。结果仅具有给定数目的对象的第一批完全hyd
如果我们知道结果,我们将需要某些属性或关系,则可以针对这些要求定制获取。对于将要访问的已知属性,我们可以将属性设置为fetch。当我们使用托管对象时,默认行为是将关系设置为false,并且关系的第一次遍历将触发对相关对象的获取。
如果遍历很少的关系或根本没有关系,那就太好了。但是,如果已知关系很可能会被遍历,我们建议将键路径设置为预取,这样就避免了在以后的时间取回数据,并且由于每次遍历都加载了错误而效率低下。
这是我们的基线提取,空闲时为17.6 MB。
但是,如果您将属性设置为仅提取用户界面中可见的那些属性,则可以将空闲内存减少到16.4 MB。现在让我们谈谈对象ID。受管对象很大且数据丰富,但是它们并不是在线程之间传递的理想选择,因此当我们要进行工作并确定满足特定条件的对象时,objectIDResultType会派上用场。这些简单的标识符可以传递给其他线程以进行进一步处理。避免了处理线程上的查找成本。
但是,如果我们需要在完整的受管对象和对象ID之间进行操作,该怎么办?像,字典结果。它们非常方便,因为它们提供了可以传递给其他线程的轻量级只读数据集。字典结果也可以进行定制以进行复杂的数据聚合,从而有助于减少通常需要拉入相关对象图的大型计算。例如groupBy在实体及其属性上具有聚合函数。让我们来看一个例子。平均幅度组按位置。首先,我们确定幅度的关键路径表达式,然后确定函数表达式的平均值。然后,我们对平均幅度进行表达式描述。最后,我们使用属性设置对地震的获取任务,以按地点获取表达式描述和地点组,并将结果设置为字典。这导致这些结果向我们显示了地震和指定地点的平均震级。我们要覆盖的最后一个结果类型是countResultType。
简单优雅且经过优化。需要我多说?我们已经优化了提取和提取。现在,让我们看一下如何改善应用程序对持久性存储中的更改的反应。 CoreData带有丰富的通知,可让您知道何时添加或删除存储或对象已保存或更改。但是我们要特别关注两个真正有用的东西。今年,我们引入了对象ID通知,除了传统的保存通知外,这些通知也可用。对象ID通知与您已经熟悉的托管对象保存通知是轻量级的对等物,该方便的通知是从持久性历史事务中出售的。让我们看看Swift中的这些新增功能。现在,受管对象上下文具有针对Swift的现代化通知。我们更新了一些旧有的东西,使它们在Swift中变得更友好,并添加了这两个新的通知,这些通知使您可以使用对象ID而不是托管对象来驱动应用程序。但这还不是我们现代化的全部。
添加了通知键
作为我们现代化工作的一部分,我们还添加了通知键,这些通知键使Swift中的通知处理变得更加容易。我们还添加了一些新键,以与您的对象ID通知一起使用。我们要讨论的另一个通知是远程更改通知。远程更改的通知非常有用,因为任何CoreData客户端都会将它们发布给您在过程中和过程外完成的所有操作。
这使您的应用程序避免轮询更改,并能够通过通知驱动相同的逻辑。并且,当启用了持久历史记录后,远程更改通知的用户信息将包含一个持久历史记录令牌,该令牌可用于获取对象ID通知。
让我们在作品中看到这一点。在这里,我们有我们的容器和应用程序,还有一个表,显示了到目前为止已捕获的持久性历史记录。
来自USGS的JSON提要上线,并且后台上下文将JSON数据提取到持久性存储中。持久的历史记录非常详细地捕获了操作。远远超出我们在这里可以看到的范围。
以前,虽然我们的应用程序需要轮询商店以查找新更改,但不需要使用远程更改通知。而是被通知已进行了更改,并且远程更改通知的用户信息有效负载具有历史记录令牌。这样我可以看到确切的操作和持久的历史记录。让我们看看随着应用程序的发展,这是如何工作的。
我们的应用程序有一些新的补充。共享扩展名。第二个应用程序利用相同的数据和方便的照片扩展名。当这些新添加的内容之一对持久性存储进行更改时,将记录该操作并将其保留在历史记录中。但是,当我们的应用程序出现在前台时,它需要轮询历史记录,这确实非常昂贵。
但是,如果启用了远程更改通知,则当我们的应用程序进入前台时,我们会收到通知。并根据我们的照片扩展名进行后续更改。还有我们的第二个应用程序。还有我们的股票扩展。当我们的应用重新启动时,它会收到通知,并可以轻松查看由于永久历史记录而发生的变化。这两个功能使您很容易知道谁,什么,何时,何地以及如何更改持久性故事。最后是关于持久历史的快速提示。关于持久性历史记录的一个相当方便的技巧是我们早些时候告诉您获取请求的说法-请确保根据您的应用程序的需求量身定制请求。在这里,我们有一个示例,说明如何调整更改请求,以便我们可以找到给定日期后特定对象ID的所有更改。首先,我们从获取持久性历史更改对象的实体描述开始。这样我们就可以使用它来构建获取请求并设置实体。然后,我们在此处设置谓词以查找对特定对象ID的更改。然后,我们创建自己的历史记录请求并设置获取请求。瞧,执行!我们的结果将仅是特定日期之后给定对象ID的那些更改。
这就是本届会议的全部内容。快速回顾。在可能的情况下进行批处理,将获取的内容定制为预期用途,并利用通知的功能和