核心数据:正确入门

核心数据是Apple为macOS和iOS设计的框架,可持久存储对象。 它本质上是SQLite的抽象。 Apple提供了有关Core Data的综合指南,但鉴于其复杂性,可能需要一段时间才能通过。 在这篇文章中,如果您要构建现代应用程序,我将介绍正确入门的基础知识。 我还将介绍核心数据的一些重要方面,这些方面可能并不明显,但在许多教程中经常被忽略。

设定

因此,您刚刚在Xcode中创建了一个新项目,并选中了“使用核心数据”复选框(如果没有,则将丢失文件,请查看下一段以获取详细信息)。

默认情况下,Xcode将与Core Data相关的方法添加到您的AppDelegate类。 如今,这样做是一种不好的做法。 在我看来,您的AppDelegate应该只管理应用程序的生命周期。 多年来,Core Data变得越来越复杂和强大,因此最好将数据存储代码放在单独的类中。 绝对从AppDelegate中删除以下内容: //标记下的所有内容:-如果使用Swift,则为核心数据堆栈; #pragma标记下的所有内容;如果使用Objective-C,则为核心数据 堆栈

未选中“使用核心数据”

如果在创建项目时未选中“使用核心数据”复选框,则您的AppDelegate应该是干净的,您只需要创建一个文件:数据模型即可。 在项目浏览器中右键单击您的主项目文件夹,然后单击“新建文件…”,选择“ iOS”>“核心数据”,然后单击“数据模型”,然后单击“下一步”,然后按照步骤保存文件。

实体

我不会花很多时间谈论创建实体和关系,因为它很简单,没有技巧,并且有很多很棒的指南,例如tutsplus博客或objc.io博客。 还可以查看江豪的关于创建实体和关系的文章。

打开.xcdatamodeld文件,您应该具有如下所示的内容:

只需单击屏幕底部的“添加实体”按钮,即可随意添加一些属性,更改实体名称并使用编辑器。 现在,最重要的部分是将您的实体映射到类。 选择您的实体,然后在导航栏中单击“编辑器”,然后单击“创建NSManagedObject子类…”:

选择对象模型,然后选择实体并创建文件。 如果使用Swift,将为您创建两个文件(如果使用Objective-C,则为四个文件)。 一个类将是NSManagedObject的子 。 此类仅应包含对象的方法,另一个应为该对象的扩展,并且应包含属性(请注意,相反的方法也可以,您可以在NSManagedObject子类上具有属性,而在扩展上具有方法)。 我的理解和对此可能是错的,因为我们将它们分开以保持清洁。 例如,您可以拥有一个插件,该插件可以在更改模型时自动重新生成实体类,而不会覆盖您的方法。

数据存储单例

现在,您应该创建一个新的单例类,该类将为您的应用程序提供数据管理方法(因为我们已从AppDelegate中删除了这些方法)。

如果您没有时间阅读我的文章,可以在这里找到一个很好的Objective-C示例:https://gist.github.com/NachoMan/922496。 但是请知道,此代码是非ARC的,因此,如果要在iOS应用中使用它,则只需摆脱–(void)dealloc方法。 如果您有时间,请查看下面的“ 线程”部分,这很重要! 苹果还展示了在Objective-C和Swift中可用的稍微复杂一点的单例。 如果您有几分钟的时间,那么让我们逐一介绍一下单例所需的方法(在Swift中):

物产

让我们从一个具有以下属性的新类开始(自行替换字符串):

ManagedObjectModel

ManagedObjectModel本质上是从.xcdatamodeld文件加载的数据模型。 只需将“ MyApp”替换为模型文件的实际名称即可。 现在您可能想知道扩展名为什么是“ momd” ,这是因为我们要引用模型的编译版本,而您无法在“项目浏览器”中查看该版本。 将以下内容添加到您的DataManager类中:

持久性商店协调员

PersistentStoreCoordinator管理数据的实际存储。 将以下内容添加到DataManager类中:

应用程序支持文件夹

让我们添加一个属性,该属性包含应用程序的应用程序支持文件夹的位置,该属性仅用于实用程序(此代码是Apple示例的简化版本):

托管对象上下文

ManagedObjectContext本质上是一个暂存器,可用于诸如获取,修改,删除对象等操作。该上下文是DataManager类的唯一部分,您将在应用程序的其余部分中使用该上下文来检索和存储数据。

苹果对托管对象上下文在线程环境中的工作方式进行了一些更改。 大概最近,添加了两个新的并发类型: MainQueueConcurrencyTypePrivateQueueConcurrencyType 。 如果未指定,则默认值为ConfinementConcurrencyType ,该类型将复制在引入这些并发类型之前可能期望的相同行为。 现在真正重要的部分是: 无法从后台线程修改 ConfinementConcurrencyTypeMainQueueConcurrencyType上下文中的检索到的对象。 前一阵子没什么大不了,但是现在大多数内置的移动应用程序都可以连接到网络,下载数据,并且我们处理异步操作和后台线程。 那么,如何保存刚刚下载的数据? 阅读下面的“ 线程”部分。

穿线

创建私有上下文

核心数据框架提供的对象上下文不是线程安全的。 为什么? 它快很多。 由于相同的原因,许多Apple iOS和OS X框架都不是线程安全的。 因此,让我们向DataManager添加一个方法,该方法返回可以在线程中安全使用的上下文。

将此私有上下文设置为主要上下文的子级。 使用单独的上下文的好处是,您可以在该上下文中执行所需的任何操作,而不会影响其他打开的上下文。

合并来自儿童上下文的更改

现在我们想要的是,当您将更改保存到此上下文时,应将其与主上下文合并并保存到磁盘。 为此,我们需要侦听通知,因此将其添加到DataManager类中:

当我们收到更改通知时将调用的处理程序,该处理程序从子上下文中获取更改并将其与主上下文合并:

使用私有上下文

您如何使用私人环境? 在下面的示例中,我从数据存储中检索实体,对其进行修改并保存:

这是一个示例,其中我进行异步请求并保存从Web服务器获取的数据:

要记住的事情

  • 绝不要在后台线程中使用您的主要上下文。
  • 您不能将对象从一个上下文传递到另一个上下文。 您可以使用对象的核心数据生成的唯一ID重新获取在一个上下文中检索到的对象: context.objectWithID(entity.objectID)