深入理解Core Data

留给我这忘事精看

Core Data 是什么?

大概八年前,2005年的四月份,Apple 公布了 OS X 10.4,正是在这个版本号中 Core Data 框架公布了。那个时候 YouTube 也刚公布。

Core Data 是一个模型层的技术。Core Data 帮助你建立代表程序状态的模型层。Core Data 也是一种持久化技术,它能将模型对象的状态持久化到磁盘,但它最重要的特点是:Core Data 不仅是一个载入、保存数据的框架,它还能和内存中的数据非常好的共事。

假设你之前以前接触过 Object-relational maping (O/RM):Core Data不是一个 O/RM,但它比 O/RM 能做的很多其它。

假设你之前以前接触过 SQL wrappers:Core
Data 不是一个 SQL wrapper。它默认使用 SQL,可是,它是一种更高级的抽象概念。假设你须要的是一个 O/RM 或者 SQL wrapper,那么 Core Data 并不适合你。

对象图管理(object graph management)是 Core Data 最强大的功能之中的一个。为了更好利用 Core Data。这是你须要理解的一块内容。

另一点要注意:Core Data 是全然独立于不论什么 UI 层级的框架。它是作为模型层框架被设计出来的。在 OS X 中,甚至在一些后台驻留程序中,Core Data 都起着很重要的意义。

堆栈

Core Data 有相当多可用的组件。

这是一个很灵活的技术。在大多数的使用情况下,设置都相当简单。

当全部的组件都捆绑到一起的时候。我们把它称作 Core Data 堆栈。这个堆栈有两个主要部分。一部分是关于对象图管理。这正是你须要非常好掌握的那一部分,而且知道怎么使用。

第二部分是关于持久化。比方,保存你模型对象的状态,然后再恢复模型对象的状态。

在两个部分之间。即堆栈中间,是持久化存储协调器(persistent store coordinator)。也被称为中间审查者。

它将对象图管理部分和持久化部分捆绑在一起,当它们两者中的不论什么一部分须要和还有一部分交流时,这便须要持久化存储协调器来调节了。

对象图管理是你程序模型层的逻辑存在的地方。模型层的对象存在于一个 context 内。在大多数的设置中。存在一个 context 。而且全部的对象存在于那个 context 中。

Core Data 支持多个 contexts,只是对于更高级的使用情况才用。注意每一个 context 和其它 context 都是全然独立的,一会儿我们将会谈到。须要记住的是,对象和它们的 context 是相关联的。

每一个被管理的对象都知道自己属于哪个
context,而且每一个 context 都知道自己管理着哪些对象。

堆栈的还有一部分就是持久了,即 Core Data 从文件系统中读或写数据。

每一个持久化存储协调器(persistent store coordinator)都有一个属于自己的持久化存储(persistent store),而且这个 store 在文件系统中与 SQLite 数据库交互。为了支持更高级的设置,Core
Data 能够将多个 stores 附属于同一个持久化存储协调器,而且除了存储 SQL 格式外,还有非常多存储类型可供选择。

最常见的解决方式例如以下图所看到的:

组件怎样一起工作

让我们高速的看一个样例。看看组件是怎样协同工作的。

在我们的文章《一个完毕的 Core Data 应用》中。正好有一个实体。即一种对象:我们有一个 Item 实体相应一个 title。每个 item 能够拥有子 items,因此,我们有一个父子关系。

这是我们的数据模型。

正如我们在《数据模型和模型对象》一文中提到的一样,在 Core Data 中有一种特别的对象——实体。在这样的情况下,我们仅仅有一个实体:Item 实体。相同的,我们有一个 NSManagedObject 的子类,叫做 Item

这个 Item 实体映射到 Item 类上。在数据模型的文章中会具体的谈到这个。

我们的程序仅有一个根 Item。

这并没有什么奇异的地方。它是一个我们用来显示底层 item 等级的 item。

它是一个我们永远不会为其设置父类的 Item。

当程序执行时,我们像上面图片描绘的一样设置我们的堆栈。一个存储,一个 managed object context,以及一个持久化存储协调器来将它们关联起来。

在第一次执行时,我们并没有不论什么 items。

我们须要做的第一件事就是创建根 item。你通过将它们插入 context 来添加管理对象。

创建对象

插入对象的方法似乎非常笨重,我们通过 NSEntityDescription 的方法来插入:

+ (id)insertNewObjectForEntityForName:(NSString *)entityName
               inManagedObjectContext:(NSManagedObjectContext *)context

我们建议你添加两个方便的方法到你的模型类中:

+ (NSString *)entityName
{
   return @“Item”;
}

+ (instancetype)insertNewObjectInManagedObjectContext:(NSManagedObjectContext *)moc;
{
   return [NSEntityDescription insertNewObjectForEntityForName:[self entityName]
                                        inManagedObjectContext:moc];
}

如今。我们能够像这样插入我们的根对象了:

Item *rootItem = [Item insertNewObjectInManagedObjectContext:managedObjectContext];

如今,在我们的 managed object context 中有一个唯一的 item。

Context 知道这是一个新插入进来须要被管理的对象,而且被管理的对象 rootItem 知道这个 Context(它有一个 -managedObjectContext 方法)。

保存改变

尽管我们已经谈到这了。但是我们还是没有接触到持久化存储协调器或持久化存储。新的模型对象—rootItem,只在内存中。假设我们想要保存模型对象的状态(在这样的情况下不过一个对象),我们须要保存 context:

NSError *error = nil;
if (! [managedObjectContext save:&error]) {
    // 啊,哦. 有发生错误了 :(
}

这个时候。非常多事情将要发生。首先是 managed object context 计算出改变的内容。

这是 context 的职责。追踪出不论什么你在 context 管理对象中做出的改变。在我们的样例中,我们到如今做出的唯一改变就是插入一个对象。即我们的 rootItem

Managed object context 将这些改变传给持久化存储协调器,让它将这些改变传给 store。

持久化存储协调器会协调 store(在我们的样例中,store 是一个 SQL 数据库)来将我们插入的对象写入到磁盘上的 SQL 数据库。

NSPersistentStore 类管理着和
SQLite 的实际交互。而且产生须要被运行的 SQL 代码。持久化存储协调器的角色就是简化调整 store 和 context 之间的交互过程。在我们的样例中,这个角色相当简单。可是,复杂的设置能够有多个 stores 和多个 contexts。

更新关系

Core Data 的优势在于管理关系。让我们着眼于简单的情况:添加我们第二个 item。而且使它成为 rootItem 的子 item:

Item *item = [Item insertNewObjectInManagedObjectContext:managedObjectContext];
item.parent = rootItem;
item.title = @"foo";

好了。相同的,这些改变只存在于 managed object context 中。一旦我们保存了 context,managed object context 将会通知持久化存储协调器。像添加第一个对象一样添加新创建的对象到数据库文件里。但这也将会更新第二个 item 与第一个 item 之间的关系。记住 Item 实体是怎样有一个父子关系的。

它们之间有相反的关系。由于我们设置第一个
item 为第二个 item 的父亲(parent)时,第二个 item 将会变成第一个 item 的儿子(child)。

Managed object context 追踪这些关系,持久化存储协调器和 store 保存这些关系到磁盘。

获取对象

我们已经使用我们的程序一会儿了,而且已经为 rootItem 添加了一些子 items,甚至添加子 items 到子 items。

然而,我们再次启动我们的程序。Core Data 已经将这些 items 之间的关系保存到了数据库文件。对象图是持久化的。我们如今须要取出根 item。所以我们能够显示底层 items 的列表。

有两种方法能够达到这个效果。我们先看简单点的方法。

当 rootItem 对象创建并保存之后我们能够向它请求它的 NSManagedObjectID。这是一个不透明的对象,能够唯一代表 rootItem。我们能够保存这个对象到 NSUSerDefaults。像这样:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setURL:rootItem.objectID.URIRepresentation forKey:@"rootItem"];

如今,当程序又一次执行时。我们能够像这样返回得到这个对象:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSURL *uri = [defaults URLForKey:@"rootItem"];
NSManagedObjectID *moid = [managedObjectContext.persistentStoreCoordinator managedObjectIDForURIRepresentation:uri];
NSError *error = nil;
Item *rootItem = (id) [managedObjectContext existingObjectWithID:moid error:&error];

非常明显。在一个真正的程序中,我们须要检查 NSUserDefaults 是否真正返回一个有效值。

刚才的操作是 managed object context 要求持久化存储协调器从数据库取得指定的对象。根对象如今被恢复到 context 中。然而,其它全部的 items 仍然不在内存中。

rootItem 有一个子关系叫做 children。但如今那儿还没有什么。

我们想要显示
rootItem 的子 item。因此我们须要调用:

NSOrderedSet *children = rootItem.children;

如今发生的是,context 标注这个 rootItem 的子 item 为所谓的故障。

Core Data 已经标注这个关系为仍须要被解决。既然我们已经在这个时候訪问了它,context 将会自己主动配合持久化存储协调器来将这些子 items 加载到 context 中。

这听起来可能非常不重要,可是在这个时候真正发生了非常多事情。

假设不论什么子对象偶然发生在内存中。Core Data 保证会复用那些对象。

这是Core Data 独一无二的功能。在 context 内,从不会存在第二个同样的单一对象来代表一个给定的 item。

其次,持久化存储协调器有它自己内部对象值的缓存。假设 context 须要一个指定的对象(比方一个子 item),而且持久化存储协调器在缓存中已经有须要的值,那么,对象(即这个 item)能够不通过 store 而被直接加到 context。

这非常重要,由于訪问 store 就意味着运行 SQL 代码。这比使用内存中存在的值要慢非常多。

随着我们遍历 item 的子 item,以及子 item 的子 item,我们慢慢地把整个对象图引用到了 managed object context。而这些对象都在内存中之后,操作对象以及传递关系就会变得很快,由于我们仅仅是在 managed object context 里操作。我们跟本不须要訪问持久化存储协调器。在我们的 Item 对象上訪问 titleparent 和 children 是很快并且高效的。

因为它会影响性能。所以了解数据在这些情况下怎么取出来是很重要的。

在我们特定的情况下,因为我们并没接触到太多的数据,所以这并不算什么。可是一旦你须要处理的数据量较大,你将须要了解在背后发生了什么。

当你遍历一个关系时(比方在我们样例中的 parent 或 children 关系)以下三种情况将有一种会发生:(1)对象已经在
context 中,这样的操作基本上是没有不论什么代价的。

(2)对象不在 context 中。可是由于你近期从 store 中取出过对象,所以持久化存储协调器缓存了对象的值。这个操作还算便宜(可是。一些操作会被锁住)。

操作耗费最昂贵的情况是(3)。当 context 和持久化存储协调器都是第一次訪问这个对象。这样的情况必须通过 store 从 SQLite 数据库取回。最后一种情况比(1)和(2)须要付出很多其它代价。

假设你知道你必须从 store 取回对象(比方你已经知道没有这些对象)。当你限制一次取回多少个对象时,将会产生非常大的不同。在我们的样例中,我们希望一次性取出全部子 items。而不是一个接一个。我们能够通过一个特别的技巧 NSFetchRequest。可是我们要注意,当我们须要做这个操作时,我们仅仅须要运行一次取出请求,由于一次取出请求将会造成(3)发生。这将总是独占
SQLite 数据库的訪问。因此。当须要显著提升性能时。检查对象是否已经存在将变得很有意义。你能够使用-[NSManagedObjectContext
objectRegisteredForID:]
来检測一个对象是否已经存在。

改变对象的值

如今,我们能够说,我们已经改变我们一个 Item 对象的 title

item.title = @"New title";

当我们这样做时,item 的 title 改变了。此外。managed object context 会标注这个对象(item)已经被改变,这样当我们在 context 中调用 -save: 时,这个对象将会通过持久化存储协调器和附属的
store 保存起来。context最关键的职责之中的一个就是跟踪改变。

从最后一次保存開始,context 知道哪些对象被插入。改变以及删除。

你能够通过 -insertedObjects-updatedObjects,
以及 –deletedObjects 方法来达到这种效果。相同的,你能够通过 -changedValues 方法来询问一个被管理的对象哪些值被改变了。这种方法正是
Core Data 可以将你做出的改变推入到数据库的原因。

当我们插入一个新的 Item 对象时。Core Data 知道须要将这些改变存入 store。那么。将你改变对象的 title 时,也会发生相同的事情。

保存 values 须要协调持久化存储协调器和持久化 store 依次訪问 SQLite 数据库。和在内存中操作对象比起来。取出对象和值,訪问 store 和数据库是非常耗费资源的。无论你保存了多少更改。一次保存的代价是固定的。而且每一个变化都有成本。这是 SQLite 的工作方式。

当你做非常多更改的时候,须要将更改打包。并批量更改。假设你保存每一次更改。将要付出非常高的代价,由于你须要常常做保存操作。

假设你非常少做保存。那么你将会有一大批更改交给 SQLite 处理。

相同须要注意的是保存操作是原子性的,要么全部的更改会被提交给 store/SQLite 数据库,要么不论什么更改都不被保存。当实现自己定义 NSIncrementalStore 基类时。这一点一定要牢记在心。要么确保保存永远不会失败(比方说不会发生冲突)。要么当保存失败时,你
store 的基类须要恢复全部的改变。否则。在内存中的对象图终于和保存在 store 中的对象不一致。

假设你使用一个简单的设置。保存操作通常不会失败。可是 Core Data 同意每一个持久化存储协调器有多个 context,所以你可能陷入持久化存储协调器层级的冲突之中。改变是对于每一个 context 的。还有一个 context 的更改可能导致冲突。Core Data 甚至同意全然不同的堆栈訪问磁盘上同样的 SQLite 数据库。

这明显也会导致冲突(比方,一个 context 想要更新一个对象的值,而还有一个 context 想要删除这个对象)。还有一个导致保存失败的原因可能是验证。Core Data 支持复杂的对象验证策略。这是一个高级话题。一个简单的验证规则可能是: Item 的 title 不能超过300个字符。可是
Core Data 也支持通过属性进行复杂的验证策略。

结束语

假设 Core Data 看起来让人害怕,这最有可能是由于它的灵活性同意你能够通过很复杂的方法使用它。始终记住:尽可能保持简单。它会让开发变得更easy,而且把你和你的用户从麻烦中解救出来。

除非你确信它会带来帮助,才去使用更复杂的东西。比方说是 background contexts。

当你開始使用一个简单的 Core Data 堆栈,而且使用我们在这篇文章中讲到的知识吧,你将非常快会真正体会到 Core Data 能为你做什么。而且学到它是怎么缩短你开发周期的。



话题 #4 下的很多其它文章

原文 Core Data Overview

译文 Core Data概述

精细校对 @shjborage

关于译者

answer-huang

iOS开发人员。Python爱好者。创业公司创业中(康大预诊)。个人博客:

http://answerhuang.duapp.com/

时间: 2024-07-29 20:58:18

深入理解Core Data的相关文章

CoreData学习:Core Data Stack(Swift)

Core Data是苹果官方提供的一套框架,用来解决对象生命周期管理.对象关系图管理和持久化等方面相关的问题.Core Data是模型层的技术,Core Data帮助你构建代表程序状态的模型层.Core Data也是一种持久化技术,它可以将模型的状态持久化到磁盘.但它更重要的特点是:Core Data不只是一个加载和保存数据的框架,它也能处理内存中的数据. 什么是Core Data? 对于Core Data框架将经常困惑,以为是数据库.其实Core Data并不是数据库.如果它不是数据库,那么C

我为什么用 SQLite 和 FMDB 而不用 Core Data

转:http://segmentfault.com/a/1190000000363392 编者注:文章的"我"是指原作者. 凭良心讲,我不能告诉你不去使用Core Data.它不错,而且也在变好,并且它被很多其他Cocoa开发者所理解,当有新人加入你的组或者需要别人接手你的项目的时候,这点很重要.更重要的是,不值得花时间和精力去写自己的系统去代替它.真的,使用Core Data吧. 为什么我不使用Core Data Mike Ash写到: 就我自己而言,我不是个狂热粉丝.我发现API是

ios开发:Core Data概述

Core Data 概述 2005年的四月份,Apple 发布了 OS X 10.4,在这个版本中 Core Data 框架发布了.Core Data本身既不是数据库也不是数据库访问框架.相反,Core Data是一个完整的数据模型解决方案.可以简单理解为对持久层的封装,使得我们可以通过可视化建立数据模型,简化数据存取.即使不懂SQL语句,也依然可以使用Core Data.因为Core Data将底层的数据库SQL语句封装成了一套API,并可通过可视化操作来建立数据库的模型和表之间的关系,它甚至

Core Data的使用(二)备

一.基础概念深入 1.NSManagedObjectContext 被管理数据上下文就像便笺簿 当从数据持久层获取数据时,相当于把这些临时的数据拷贝写在便笺簿上,然后就可以随心所欲的修改这些值. 通过上下文,可以对数据记录NSManagedObject进行添加删除更改,记录更改后支持撤销和重做. 除非你保存这些数据变化,否则持久层的东西是不会变化. 通常我们将 controller 类或其子类与 Managed Object Context NSManagedObjectContext绑定,这样

Core Data 版本数据迁移

Core Data版本迁移基础 通常,在使用Core Data的iOS App上,不同版本上的数据模型变更引发的数据迁移都是由Core Data来负责完成的.这种数据迁移模式称为Lightweight Migration(可能对于开发人员来说是lightweight),开发人员只要在添加Persistent Store时设置好对应选项,其它的就交付给Core Data来做了:从命名上可以看出这两个选项分别代表:自动迁移Persistent Store,以及自动创建Mapping Model.自动

不再为Core Data多线程编程而头疼

by Saul Mora原文链接:http://www.cimgf.com/2011/05/04/core-data-and-threads-without-the-headache/ 我知道我曾经提到我要写一篇关于定制fetch requests的文章,然而,在我为Active Record Fetching project(现在已经改名为MagicalRecord)编写了一些代码之后,我觉得写一篇关于fetching–threading.的文章更好一些. 当大多数cocoa开发者提到Core

使用Core Data应避免的十个错误

Core Data是苹果针对Mac和iOS平台开发的一个框架,主要用来储存数据.对很多开发者来说,Core Data比较容易入手,但很难精通,如果没有正确的学习方法,你将很难真正理解它,更不用说精通了.很多开发者常常在这方面犯一些错误,而这篇文章列出了开发者在iOS开发过程中使用Core Data常见的一些错误,并对如何避免这些错误进行了分析.   1.不了解关键术语 对于iOS开发者来说,会使用Core Data是一项必备技能. 没有它,很多app都不会存在.当在互联网上四处搜索Core D

iOS开发过程中使用Core Data应避免的十个错误

原文出处: informit   译文出处:cocoachina Core Data是苹果针对Mac和iOS平台开发的一个框架,主要用来储存数据.对很多开发者来说,Core Data比较容易入手,但很难精通,如果没有正确的学习方法,你将很难真正理解它,更不用说精通了.很多开发者常常在这方面犯一些错误,而这篇文章列出了开发者在iOS开发过程中使用Core Data常见的一些错误,并对如何避免这些错误进行了分析.  1.不了解关键术语 对于iOS开发者来说,会使用Core Data是一项必备技能.

Core Data的数据迁移

原文地址:http://blog.csdn.net/jasonblog/article/details/17842535 Core Data版本迁移基础 通常,在使用Core Data的iOS App上,不同版本上的数据模型变更引发的数据迁移都是由Core Data来负责完成的. 这种数据迁移模式称为Lightweight Migration(可能对于开发人员来说是lightweight),开发人员只要在添加Persistent Store时设置好对应选项,其它的就交付给Core Data来做了