掌握CoreData(第16部分,多线程并发策略父级-子级用例2)

使用亲子策略放弃临时更改

在本部分中,我们将看到如何使用“父—子托管对象上下文”策略对UI临时更改核心数据对象。 首先下载启动项目。

注意:有关父级-子托管对象上下文策略,请参见第14和15部分。

当应用程序首次启动时,您将重定向到User屏幕,如图1所示。点击details按钮将执行两项任务。 首先,它将增加登录尝试(实体)计数器,然后将您重定向到“用户详细信息”屏幕,在该屏幕上将显示用户完整信息,用户可以在该屏幕中编辑信息,如图2所示。

图2是“用户详细信息”屏幕,用户可以在其中编辑其信息。 在背面,它移至上一个屏幕(用户屏幕),并放弃临时更改。 点击保存按钮将更新对核心数据的更改

项目结构

描述临时变更问题的项目结构如图3所示。

AppDelegate →负责创建核心数据堆栈。

ViewController→包含显示用户的逻辑,用户可以点击详细信息按钮以重定向到用户详细信息屏幕。 (用户画面)

UserDetailViewController→包含用于显示来自Core Data的用户详细信息的逻辑以及用于在用户编辑信息后点击保存按钮时将更改保存到数据库的逻辑

TemporaryChanges.xcdatamodeld→包含两个实体

  1. 用户→包含属性firstnamelastNameid
  2. LoginAttempt→包含属性计数

问题

首次启动应用程序时,具有firstnamelastname用户(如果不存在)将保存到持久性存储中,并且其f firstname将显示在标签上,如图4所示。

现在,当您点击详细信息按钮时,它将执行两项任务

  1. 它将LoginAttempt count属性增加到+1,并通过调用save方法将其保存到持久性存储中
  2. 使用情节提要segue重定向到详细信息屏幕

现在点击“用户”屏幕上的“ 详细信息”按钮,它将重定向到用户详细信息屏幕,如图6所示。在此屏幕中,显示了用户详细信息。 用户可以通过单击保存按钮来更新其信息并将其保存到数据库中,也可以通过单击后退按钮放弃其临时更改。

更改用户详细信息,如图7所示,我们将firstnameAli更改为Ali temp并且还更新了lastname

当用户在任何文本字段上键入内容时,它将更新NSMangedObject类型的User核心数据对象,该对象是控制器的实例属性。 现在,当用户完成firstname文本字段时,此更新也将反映到位于上下文中的user对象

众所周知, User对象是在主线程上下文中填充的,当我们执行临时更改时,它将在上下文中更新该对象,并且当我们点击后退按钮时,更新的值仍然存在于上下文中。 现在,点击后退按钮后,将出现用户屏幕。 我们知道,当我们点击详细信息按钮时,它将首先增加计数器并通过调用save方法来调用save方法,这还将把用户临时更改也推送到永久存储,并使用更新后的值重定向到User Detail屏幕。

由于受管对象上下文是单例对象save方法,因此会将临时更改推送到持久性存储

正如您在更新的屏幕中看到的那样,显示用户临时更改。 问题是,当用户执行后退按钮时,我们需要放弃更改。 我们可以通过三种方式做到这一点

  1. 使用父级—子级策略(此部分的封面)
  2. 撤消经理(下一部分)
  3. 手动跟踪更改(不会涵盖)

使用流程图了解问题

如您在图11中看到的,当应用程序首次启动时,用户信息将存储到持久性存储中。 当您点击详细信息按钮时,它将显示直接与UI交互的主要上下文中的用户详细信息

现在,用户在用于填充“用户详细信息”屏幕的直接coredata实体上编辑信息并更新其信息。 那时,由于User Detail使用核心数据用户对象(主要上下文中)填充其视图。 这种变化也将反映到上下文中。 假设用户想通过单击后退按钮放弃此更改,我们必须具有某种机制来放弃此更改,而当前应用程序不支持该更改。 由于主上下文是整个应用程序的单例,因此有可能保存主上下文方法将在应用程序的任何地方调用,这会将更改合并到持久性存储中,如图12所示。

当点击后退按钮时,再次进入该屏幕。 假设未调用save方法,因此更改不会反映到持久性存储中。 但是UserDetailViewController是从数据库UserDetailViewController用户详细信息。 它将要求单例主线程上下文给我用户数据。 上下文首先查看其缓存(如果发现)将发送临时更新的用户对象。 这就是问题所在,那么我们如何才能以简单的方式丢弃临时更改

了解使用流程图的解决方案(父子配置)

如您在图14中看到的。 我们正在使用也在主线程上创建的子上下文填充用户详细信息屏幕。 作为我们的规则,我们需要在主线程上创建与UI相关的任务上下文。 我们在主线程上创建了父级和子级

我们在主线程上创建了父级和子级。

如图14所示,由于我们是从子上下文中的对象填充“用户详细信息”屏幕的,因此“用户”屏幕中的更改将仅反映到子上下文。

您在使用多个托管对象上下文时记得的一件事是,每个上下文包含自己的nsmanagedObject副本。

当用户点击后退按钮时,我们只是丢弃我们的子上下文,而用户现在是“用户”屏幕。 就像我在以前的教程中所说的那样,不要在需要时仅将子上下文创建为单例 ,并在完成后销毁它。 如图15所示,用户点击后退按钮后,我们将完全丢弃子上下文,因为它在销毁它时将包含所有临时信息,也会销毁所有临时更改,并且您可以看到父存储和持久性存储都包含原始数据

如您在图16中看到的。 当您再次进入用户详细信息屏幕时。 UserDetailViewController将再次创建一个子上下文,并且子上下文在创建时始终具有与父上下文相同的状态,因此不会显示用户之前的更改。

让我们举例说明用户点击保存而不是返回。 如图17所示,当用户点击保存按钮时,子项需要将这些更改合并到父项中,然后该父项会将更改推送到持久性存储中。 因此,需要执行两个save方法。

编码

因此,我们首先删除该应用程序,如图18所示,我们做了很多事情

  1. 由于我们正在处理与UI相关的任务,因此在主线程上创建了子上下文
  2. 创建父级和母级都在主要上下文中的父级子级策略。 父上下文是应用程序的单例,而上下文则充当控制器的实例属性。 因此,在控制器取消分配之后,它也会丢弃此上下文
  3. 从子托管对象上下文中获取了对象,并且我们还使用子上下文填充了核心数据用户对象(以及控制器的实例属性)
  4. 最后,当用户点击后退按钮控制器时,该控制器将弹出,子上下文将被所有临时更改丢弃

现在运行应用程序,并再次执行与上面所示相同的流程。 您将看到问题将解决,现在我们实际上丢弃了用户对象上的临时更改。

最后,当用户点击保存按钮时,我们实际上首先在子上下文上使用save方法将更改推送到父上下文中,然后通过在父上下文上调用save方法将更改推送到持久性存储中

摘要

托管对象上下文是用于处理托管对象的内存暂存器。我们使用多个托管对象上下文来执行两种类型的任务。 我们在上一部分中谈到了一项任务。 在这一部分中,我们使用父子上下文来丢弃上下文中的临时更改。

下一步是什么

在下一部分中,我们将研究如何使用UndoManager解决上下文中发生的临时更改问题

有用的链接

https://code.tutsplus.com/tutorials/core-data-from-scratch-concurrency–cms-22131

https://www.raywenderlich.com/7586-multiple-managed-object-contexts-with-core-data-tutorial