《驾驭Core Data》 第一章 Core Data概述
《驾驭Core Data》系列教程综合了《Core Data for iOS》,《Learning Core Data for iOS》,《Core Data Programming Guide》,以及相关的博客资料。共包含14章内容。希望本系列教程能对学习Core Data的iOS开发者有所帮助。
本文由海水的味道编译整理,请勿转载,请勿用于商业用途。
当前版本号:0.0.5
第一章 Core Data概述
Mac OS X 10.4 Tiger首次引入Core Data,旨在为存储与获取应用程序的模型数据提供统一的框架。Mac OS X 10.5 Leopard又对Core Data进行了提升。在这之后,苹果公司又将Core Data应用于iPhone OS 3.0及以上版本的iOS设备上。虽然Core Data在一定程度上缓解了处理模型数据的复杂性。但掌握Core Data 也绝非易事。
本章首先简要介绍了Core Data的发展历史,然后从较高层面对Core Data进行概括,在最后一节,列举了一些iOS开发者如何利用Core Data提供的功能简化模型处理的真实案例。
Core Data的前身
当前Core Data的绝大部分功能之前是以企业对象框架EOF(Enterprise Objects Framework)存在的。EOF由NeXT公司开发。作为WebObjects一部分,它被用于访问存储在关系数据库中的数据。为了节省开发者编写访问数据库的底层代码。EOF利用对象关系映射技术,为访问面向对象的类结构数据提供了一套机制。
相对于需要为访问数据库与获取数据库表行和表列编写代码,对象关系映射将数据库表映射为类,表列映射为类的属性,表行映射为类的实例。因为EOF在后台处理了这些映射细节,所以你可以改变底层数据库或类下的存储机制而不用担心修改访问类的代码。
Core Data的今世
Core Data大部分功能都脱胎于EOF,但需要强调一点,Core Data本身既不是数据库也不是数据库访问框架。相反,Core Data是一个完整的数据模型解决方案。它允许对象图(object graph)的可视化设计;使用代码创建和查询对象图中的对象;以及使用代码将对象持久化到磁盘。
虽然一般使用Core Data是将对象存储到SQLite数据库,但使用非数据库存储方式如XML文件、二进制文件,甚至是为满足具体需求而完全由开发者自行创建的持久化存储文件也是允许的。在桌面平台,苹果公司提供了SQLite、XML以及二进制存储方式供你选择。而在iOS平台,因为不支持XML存储方式,所以大部分iOS app会选择SQLite存储。
另一点需要强调的是:Core Data支持的模型可用于存储所有类型的数据。并不一定要求是通常认为的数据库风格数据。Core Data可以存储诸如手术中医生的病人记录、公司财务应用的发票和财务信息。使用Core Data存储iOS绘图应用的矢量图形信息;社交应用的账号和搜索记录、游戏记录和需要持久化的状态信息,也是极其简单。
为什么使用Core Data
Core Data框架提供了一种稳定且快速访问数据模型的方式。它不需要大量的内存与处理器开销。
你可能会想,“我只是想持久化一些数据,何必这么劳师动众?”,当然,你可以使用类似FMDB的包装类编写自己的数据库接口,一开始这些接口可能会工作的很好。但是当你的需求发生了变更或者你需要加入新的功能,比如需要在设备之间保持数据同步;不影响界面响应的情况下,处理大量数据的导入;保证在旧的iPhoen设备上流畅运行前提下,支持撤销和数据验证,诸如此类的复杂需求。
幸运的是,所有这些复杂的工作已经包含在了Core Data框架之中。即使你的app并不需要处理成千上万的数据,使用Core Data来保证软件的扩展性与性能也是非常有价值的。
关系管理
由于Core Data前身是为了方便关系型数据库的使用,所以Core Data的特色之一就是它可以管理对象之间的关系。使用XCode的可视化数据模型设计器,对象之间的连接关系可以使用一对一、一对多或多对多的形式定义。
你可以对定义的关系设定规则。有一些特殊的关系必须要对其定义规则(比如一笔财务交易必须关联一个银行账户;一家公司必须至少有一位经理)。当在保存时,新建的对象被保存到底层的存储文件中。如果对象没有满足既定的规则,框架将会拒绝接受此对象,由此以维护对象图的完整性。
类似地,如果你改变了关系的一端,Core Data会自动处理关系另一端的变化。比如将A医生的一位病人转调给B医生。则两位医生相关联的病人列表都将相应地被自动更新。当数据模型中提供的关系规则被正确地设定,则删除了财务应用中的一个银行账户,所有与该账户相关的财务交易也会被自动删除。
NSManagedObject对象与数据验证
当使用Core Data时,你所操纵的对象被称为NSManagedObject对象,标准的NSManagedObject对象除了之前提到的关系管理功能外,还支持对数据的验证。
对象的验证发生在对象的属性上。你可以对模型中的单个属性设置规则。比如一个人的薪水不可以低于0。或者在多个属性之间设定自定义规则。比如病人如果小于特定的年龄则不可能会有孩子。
同样,如果你尝试使用一个无法通过验证测试的值更新对象,Core Data就会报错,以防止你保存不正确的数据。
撤销与状态管理
Core Data会自动地维护一个撤销栈。如若必要,即使对象已被保存到持久化存储文件,对对象的修改也可以被撤销。同样,这也是自动完成的,你不需要编写任何的撤销代码。
由于桌面平台与移动平台的底层框架代码完全相同,所以一旦你学会了如何在iOS平台使用Core Data,你就已经扫清了在桌面平台使用Core Data的大多数障碍。话虽如此,iOS平台相对于桌面平台的Core Data,实际还是会有一些区别。
iOS平台与桌面平台的Core Data之间的区别
一个最大的区别是iOS平台的Core Data不支持绑定(Bindings)。在桌面平台,绑定利用KVO和KVC保持UI项与模型对象或属性之间的连接。使用绑定技术,你可以在桌面平台构建强大的Core Data应用程序而无须编写任何代码。你只需简单地使用XCode的Interface Editor绑定类似表格视图和文本域UI项,通过对象和数组控制器自动从Core Data存储区分批提取NSManagedObject对象。
而在iOS平台,Core Data对这类即时满足(instance gratification)风格的绑定功能不做支持。相反的,你需要直接使用Core Data框架的各部分功能为用户界面提供相关数据。
NSFetchedResultsController
为了优化数据提取的过程,苹果公司单独为iOS平台的Core Data引入了NSFetchedResultsController类。顾名思义,NSFetchedResultsController(在第五章将会有一个完整的章节讨论该类)被用作控制器层的类,帮助视图与从持久化存储文件提取的数据之间的交互。它主要是用来充当UITableiView的数据源,负责调整UITableiView行与节(section)的显示数目,以及为各表格行提供内容。
NSFetchedResultsController以尽可能高效的方式消除一次性将所有查询结果载入内存的需要。它会自动处理当前不会被访问到的对象。这让处理大数量的对象更容易。如果你在表格视图中使用NSFetchedResultsController对象显示一个包含上千个对象的容器。那么在特定时刻,只会有一小部分对象将被载入内存。
Core Data案例研究
MoneyWell for iPhone
MoneyWell是一个个人理财应用,用于追踪个人的收支情况与管理现金流,用户通过查看所有可用的收支类别来管理可用的现金。当MoneyWell还在设计中时,Mac OS X Tiger与Core Data还刚发布不久。MoneyWell中的数据关系相当复杂,而Core Data的使用极大简化了模式设计与开发过程。2007年10月17日,Steve在苹果网站的一封公开信上宣布适用于iPhone设备的SDK将会于2008年2月提供给第三方开发商。听闻这一消息后,我们相信如果将MoneyWell一直到iPhone上,它将会是iPhone设备上最棒的理财应用。可问题是第一次发布的Core Data for iOS还不对开发者开放。所以我们推迟了MoneyWell for iPhone的开发。我们相信苹果公司会将Core Data迁移到移动设备上。果不其然,Core Data在iOS 3.0 SDK中被引入。
Core Data for iOS的出现解决了许多开发难题。比如在MoneyWell for iPhone中创建模型层只需简单地复制Xcode数据模型到新的项目。此外,MoneyWell的Mac版本和iOS版本之间可以共享相同数据文件,这让我们可以使用已有的所有测试文档。苹果公司对iOS平台Core Data的性能改进也让我们受益良多。贯穿于开发过程中的数据处理、数据持久化,以及对象图维护已变得非常简单,我们可以专注于更重要的问题,比如创建更好的用户体验。而对于我们来说,最大的获益无疑是Core Data结合UITableView的使用。想象一下,一个典型的用户可能有积累数年的财务数据,这无疑会消耗移动设备的大量内存。如果没有Core Data,我们不得不编写复杂的分页算法或者大量的SQLite查
询。而使用Core Data,我们可以查询一个分类的交易,并且依靠Core Data智能的数据分页能力,可以按需将数据载入或载出内存。
使用Core Data轻松地节省了我们五个月甚至更多的开发时间。
—Kevin Hoctor and Michael Fey, No Thirst Software LLC
Calcuccino
Calcuccino是一个程序员专用计算器,它使用iPhone的菜单和滑动手势改善传统计算器功能,且配备了让输入更快速可靠的大按钮。
在Calcuccino中使用Core Data的这项决定不是特别明显,因为Calcuccino没有处理大数据量的需要,但是它需要存储用户的计算历史,以让用户可以在以后可以查阅或者重用。同时软件也需要保持当前的计算状态,当用户从另一个应用切回当前应用,它的计算状态应该被恢复。在Core Data之前,这些需求我们是通过使用XML文件持久信息实现的,但这导致需要为处理XML编写许多代码。
在Xcode中Core Data提供了一个数据模型设计器,它被用来构建诸如存储历史步骤的HistoryStep实体和当前计算状态的OpStep实体。对于计算引擎本身,我们定义了一个CalcValue类,该类是一个基本的数字类型(类似于Cocoa的NSNumber类,不过它新增了整数位数和格式化功能),CalcValue的使用贯穿整个项目。GeneralValue实体实例生成的所有CalcValue实例都被存储在Core Data存储区。
Calcuccino的数据模型很好的展现了应用的需求。应用中有一个处理Objective-C类与Core Data之间交互的CalcStore类。由于Objective-C与实体之间的映射仍然需要编写映射代码,这点让我有些失望。这和将Objective-C类转换为XML或将XML映射为Objective-C类的代码没有什么不同。我们迟疑是否需要从原始的NSManageObjects转向可以减少映射代码的自定义类。
以我们使用Core Data的经验来说,如果问我们是否还会使用Core Data?答案是肯定的。我们相信这个决定是正确的。当需要在表格视图中展示保存在Core Data中的计算历史数据,并且按天进行分组时,只需要使用NSFetchedResultsController几行代码就可以轻松完成,这真是太省心了。一旦我们开发了一些Calcuccino版本,我们就需要使用Core Data的版本控制功能来进行数据库的版本控制。我们希望我们将继续相信我们做出了正确的决定。
—Andy Dean, Cambridge Coders Ltd
美联社新闻Associated Press
当要着手开发Associated Press的时候,我知道使用Core Data是一种必然。如果尝试使用其他类型的持久层,我们所处理的数据量则太多了。另外基于对异步数据传输的需求,Core Data无疑是最佳选择。
通过设置一个直接将数据映射到Core Data模型的数据解析器,然后在后台线程中保存这些数据,用户体验就可以大幅提升。
当我们开始设计应用时,我们了解到与服务器通讯的接口将被多个应用使用,所以我们将接口设计成了一个单独库。通过这样的设计只需要将数据加入到Core Data持久层。我们能够为任何在其上构建的应用提供一个简单通用的接口,每一个应用只需关注Core Data中底层数据的变化。这样做能够保持app和库之间的接口代码的最小的耦合。
实际上现在我们已经使用该库超过一年,没有出现什么问题。同时也应用在十多个应用中,表现非常不错。如果我们使用其他的持久化引擎,我怀疑我们还需要面对性能与内存问题的挑战。
—Marcus S. Zarra, Zarra Studios LLC