`
yidongkaifa
  • 浏览: 4063021 次
文章分类
社区版块
存档分类
最新评论

Core Data学习

 
阅读更多

Core dataCocoa中处理数据,绑定数据的关键特性,其重要性不言而喻,但也比较复杂。Core Data相关的类比较多,初学者往往不太容易弄懂。我近期花了一周多时间,才基本入门,今天将一些要点整理出来分享:

一、Core Data框架的概述

Core DataCocoa里面一套非常受欢迎的框架,从Mac OS X 10.4提供以来,在10.5中引入了完善的schema迁移机制,再到iPhone OS 3.0时被引入Cocoa Touch,这套完善的框架都被认为是管理大量结构化数据所首选的Cocoa框架,尤其是因为使用Core Data能大大减少需要手工编写的代码量,就使它更受开发者欢迎了。

不过最近却出现了一些不同的声音,先是传出消息说Aperture 3.0抛弃了Core Data,改为直接操作SQLite数据库(大家联想到Apple Mail 3.0也是直接用SQLite,没有用Core Data),但因为都是Apple内部的决定,大家只能凭空猜测理由;接下来,NetNewsWire的开发者Brent Simmons也说在NetNewsWire for iPhone里从Core Data转向用FMDB来操作SQLite数据库(FMDBGus Mueller编写的一层很薄的SQLiteObjective-C下的封装)Brent给的理由就很充分了:他要做的很多操作都是对数据批量进行的,其实不需要把所有数据都保存在内存里遍历执行,那样更慢,直接交给数据库,往往一条语句就搞定了,简洁而且快速。

然后JonathanWolfRentzsch也对此深表赞同 ,并推荐Aaron HillegassBNRPersistence框架,这个框架用Tokyo Cabinet提供了一个类似Core Data接口的数据持久化方案,最大的优点是比Core Data快得多,根据Aaron自己的测试,常见的操作都要快10 - 20倍。其实快这么多也可以理解,毕竟BNRPersistence要比Core Data轻量得多,支持的功能也少很多,加上Tokyo Cabinet这样的Key-value数据库在处理适合它的操作时,多数要比SQLite这样的关系型数据库要快。

所以突然Core Data就有点被墙倒众人推的意思,好像以前大家都知道它不好用,但都不好意思说,直到突然有经验足够丰富的开发者开头,就一涌而上开始骂了。我个人的观感是Core Data作为官方方案,给开发提供的许多便利还是不可小视的,但考虑学起来确实也不容易(所以才有人专门写本书讲Core Data ),所以新上手的Cocoa程序员不妨先考虑一下BNRPersistence, FMDB这样的方案。

那是否Core Data就该被抛弃呢?目前的争议其实有点像Web开发里到底该不该用ORM, 区别是大多数Web开发者因为历史原因,对直接进行数据库操作有偏好(或者说,本能地反感ORM),而多数Cocoa开发者则坚定支持用对象操作数据,所以长远来看,数据持久化方案在Cocoa (Touch)开发里少不了,唯一的疑问是Apple会不会继续改进Core Data的性能和接口,以拉近与第三方方案的差距,只要Apple还在不断改进,Core Data就有学习的必要。

我个人对Core Data的怨念主要是在要写的“胶水代码”上:有的时候为了方便与界面的bindings操作,不得不给Model写大量重复的胶水代码,所以如果第三方的方案如果在这方面有简化,我很乐意改用。

<wbr style="word-wrap:break-word"></wbr>

二、Core DataSQLite数据库

并非严格的说, CoreData是对SQLite数据库的一个封装。

在苹果iPhoneiPad上,你可以存放数据在以下三个地方

其一是:一个或多个文件

其二是:应用的属性列表(NSUserDefaults

其三是:内置的数据库(SQLite

通常SQLite数据库操作的基本流程是,创建数据库,再通过定义一些字段来定义表格结构,可以利用sql语句向表格中插入记录,删除记录,修改记录,表格之间也可以建立联系。

这个过程出现了,表格的结构(schema),所有表格的结构和相互联系构成整个数据库的模型,数据库存放的方式(可以是文件或者在内存),数据库操作, sql语句(主要是查询),表格里面的记录,下面将上面说的文字,CoreData的类作个对应:

SQLite数据库操作

VS

CoreData的类

表格结构

-->

NSEntityDescription

数据库中所有表格和他们的联系

-->

NSManagedObjectModel

数据库存放方式

-->

NSPersistentStoreCoordin<wbr style="word-wrap:break-word">ator</wbr>

数据库操作

-->

NSManagedObjectContext

查询语句

-->

NSFetchRequest

表格的记录

-->

NSManagedObject

可能上面的对应关系并非十分严格,但确实可以帮助理解.

下面再看看CoreData的类

首先是NSEntityDescriptionNSManagedObjectModel这两个非常重要的类

NSEntityDescription用来定义表格结构,所以你就可以理解NSManagedObjectModel中的setEntities:(NSArray *)entities函数大概有什么用了,通常,定义model,是用文件CoreData.xcdatamodel,可以图形化的操作.这类似用nib来创建界面。

例如:建个工程,使用core<wbr style="word-wrap:break-word"></wbr>data,模拟器运行之后,程序对应的document目录出现一个CoreData.sqlite.可以利用sqlite3命令来查看里面的表格结构:

用命令行sqlite3 CoreData.sqlite进入

>.tables

ZEVENT<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"></wbr></wbr></wbr></wbr></wbr></wbr></wbr>Z_METADATA<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"></wbr></wbr></wbr>Z_PRIMARYKEY

可以看到有表格ZEVENT,对应的CoreData.xcdatamodel文件有名字叫EventEntity

<wbr style="word-wrap:break-word"></wbr>

>.schema ZEVENT

CREATE TABLE ZEVENT ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZTIMESTAMP TIMESTAMP )

对应的Event中有属性timeStamp,可以看到,相应的ZEVENT表格中有字段TIMESTAMP

<wbr style="word-wrap:break-word"></wbr>

> select * from ZEVENT

1|1|1|306295807.974966

2|1|1|306295810.981875

3|1|1|306295811.982537

这表格有三个记录,可以用来初始化三个NSManagedObject,修改了NSManagedObject, save之后也修改了表格记录。

你可以在CoreData.xcdatamodel添加新的entity,之后用sqlit3命令来查看数据库的变化。

三、深入认识Core Data框架

下面先给出一张类关系图,让我们对它有个总体的认识。


在上图中,我们可以看到有五个相关模块:

<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><span style="word-wrap:break-word">1、被管理对象模型(Managed Object Models)</span></wbr></wbr></wbr>

<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"></wbr></wbr></wbr>Managed Object Model是描述应用程序的数据模型,这个模型包含实体(Entity),特性(Property),读取请求(Fetch Request)等。多数Core Data的功能依赖于你创建的,用来描述程序的实体及其属性、关系的模型图。

<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><span style="word-wrap:break-word">2</span></wbr></wbr></wbr>、被管理对象和上下文(Managed Objects and Contexts)

Managed Object Context参与对数据对象进行各种操作的全过程,并监测数据对象的变化,以提供对undo/redo的支持及更新绑定到数据的UI。使用Core Data的框架,大多数的功能都可以自动实现,因为我们有managed object context(管理对象的上下文,有时直接叫"Context")。managed object context就像是一个关卡,通过它可以访问框架底层的对象——这些对象的集合我们称之为"persistence stack"(数据持久栈)。managed object context作为程序中对象和外部的数据存储的中转站。栈的底部是persistence object stores(持久化数据存储)

<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><span style="word-wrap:break-word">3、持久化存储助理(Persistent Store Coordinator)</span></wbr></wbr></wbr>

Persistent Store Coordinator相当于数据文件管理器,处理底层的对数据文件的读取与写入。一般我们无需与它打交道。之前提到过,程序中的对象和外部存储的数据通过Core Data框架中的一系列对象进行协调,这一系列的对象总的被称为持久存储栈(Persistence stack)。在栈顶是被管理对象上下文(Managed object context),而栈底是持久化对象存储层(Persistence object store)。在它们之间就是持久化存储助理。

<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><span style="word-wrap:break-word">4、数据对象(<span lang="EN-US" style="word-wrap:normal; word-break:normal">Managed Object ——MO)</span></span></wbr></wbr></wbr>

<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word">Managed Object</wbr></wbr></wbr>数据对象,与Managed Object Context相关联。

<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><span style="word-wrap:break-word">5、控制器<span lang="EN-US" style="word-wrap:normal; word-break:normal">Controller</span></span></wbr></wbr></wbr>

<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word">图中绿色的<span lang="EN-US" style="word-wrap:normal; word-break:normal">Array Controller, Object Controller, Tree Controller</span>这些控制器,一般都是通过<span lang="EN-US" style="word-wrap:normal; word-break:normal">control+drag</span>将<span lang="EN-US" style="word-wrap:normal; word-break:normal">Managed Object Context</span>绑定到它们,这样我们就可以在<span lang="EN-US" style="word-wrap:normal; word-break:normal">nib</span>中可视化地操作数据。</wbr></wbr></wbr>

这些模块是怎样运作的呢?

1、应用程序先创建或读取模型文件(后缀为xcdatamodeld)生成NSManagedObjectModel对象。Document应用程序是一般是通过NSDocument或其子类NSPersistentDocument)从模型文件(后缀为xcdatamodeld)读取。

2、然后生成NSManagedObjectContextNSPersistentStoreCoordin<wbr style="word-wrap:break-word">ator</wbr>对象,前者对用户透明地调用后者对数据文件进行读写。

3NSPersistentStoreCoordin<wbr style="word-wrap:break-word">ator</wbr>负责从数据文件(xml, sqlite,二进制文件等)中读取数据生成Managed Object,或保存Managed Object写入数据文件。

4NSManagedObjectContext参与对数据进行各种操作的整个过程,它持有Managed Object。我们通过它来监测Managed Object。监测数据对象有两个作用:支持undo/redo以及数据绑定。这个类是最常被用到的。

5Array Controller, Object Controller, Tree Controller这些控制器一般与NSManagedObjectContext关联,因此我们可以通过它们在nib中可视化地操作数据对象。

四、Model class

模型有点像数据库的表结构,里面包含Entry, 实体又包含三种PropertyAttribute(属性),RelationShip(关系),Fetched Property(读取属性)。Model class的名字多以"Description"结尾。我们可以看出:模型就是描述数据类型以及其关系的。

主要的Model class有:

Model Classes

Managed Object Model

NSManagedObjectModel

数据模型

Entity

NSEntityDescription

抽象数据类型,相当于数据库中的

Property

NSPropertyDescription

Entity特性,相当于数据库表中的一列

<wbr style="word-wrap:break-word">&gt;<wbr style="word-wrap:break-word">Attribute</wbr></wbr>

NSAttributeDescription

基本数值型属性(如Int16, BOOL, Date等类型的属性)

<wbr style="word-wrap:break-word">&gt;<wbr style="word-wrap:break-word">Relationship</wbr></wbr>

NSRelationshipDescriptio<wbr style="word-wrap:break-word">n</wbr>

属性之间的关系

<wbr style="word-wrap:break-word">&gt;<wbr style="word-wrap:break-word">Fetched Property</wbr></wbr>

NSFetchedPropertyDescrip<wbr style="word-wrap:break-word">tion</wbr>

查询属性(相当于数据库中的查询语句)

<wbr style="word-wrap:break-word"></wbr>

1Entity - NSEntityDescription

Entity相当于数据库中的一个表,它描述一种抽象数据类型,其对应的类为NSManagedObject或其子类。

NSEntityDescription常用方法:

+insertNewObjectForEntity<wbr style="word-wrap:break-word">ForName:inManagedObjectContext:</wbr>//工厂方法,根据给定的Entity描述,生成相应的NSManagedObject对象,并插入ManagedObjectContext中。

-managedObjectClassName//返回映射到EntityNSManagedObject类名

-attributesByName//以名字为key, 返回Entity中对应的Attributes

-relationshipsByName//以名字为key, 返回Entity中对应的Relationships

<wbr style="word-wrap:break-word"></wbr>

2Property – NSPropertyDescription

PropertyEntity的特性,它相当于数据库表中的一列,或者XML文件中的value-key对中的key。它可以描述实体数据属性(Attribute)Entity之间的关系(RelationShip),或查询属性(Fetched Property)

<wbr style="word-wrap:break-word"></wbr>

<wbr style="word-wrap:break-word">&gt; Attribute - NSAttributeDescription</wbr>

Attribute存储基本数据,如NSString, NSNumber or NSDate等。它可以有默认值,也可以使用正则表达式或其他条件对其值进行限定。一个属性可以是optional的。

<wbr style="word-wrap:break-word"></wbr>

<wbr style="word-wrap:break-word">&gt; Relationship - NSRelationshipDescriptio<wbr style="word-wrap:break-word">n<wbr style="word-wrap:break-word"></wbr></wbr></wbr>

Relationship描述EntityProperty之间的关系,可以是一对一,也可以是一对多的关系。<wbr style="word-wrap:break-word"></wbr>

<wbr style="word-wrap:break-word"></wbr>

<wbr style="word-wrap:break-word">&gt; Fetched Property - NSFetchedPropertyDescrip<wbr style="word-wrap:break-word">tion</wbr></wbr>

Fetched Property根据查询谓词返回指定Entity的符合条件的数据对象。

上面说的比较抽象,举个例子来说,见图:

<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word">我们有一个<span lang="EN-US" style="word-wrap:normal; word-break:normal"><wbr style="word-wrap:break-word">CocoaDataDemo.xcdatamodeld</wbr></span>模型文件,应用程序根据它生成一个<span lang="EN-US" style="word-wrap:normal; word-break:normal"><wbr style="word-wrap:break-word">NSManagedObjectModel</wbr></span>对象,这个模型有三个<span lang="EN-US" style="word-wrap:normal; word-break:normal">Entity</span>,每个<span lang="EN-US" style="word-wrap:normal; word-break:normal"><wbr style="word-wrap:break-word">Entity</wbr></span>又可包含<span lang="EN-US" style="word-wrap:normal; word-break:normal">Attribute</span>、<span lang="EN-US" style="word-wrap:normal; word-break:normal">Relationship</span>和<span lang="EN-US" style="word-wrap:normal; word-break:normal">Feteched Property</span>三种类型的<span lang="EN-US" style="word-wrap:normal; word-break:normal"><wbr style="word-wrap:break-word">Property</wbr></span>。在本例中,<span lang="EN-US" style="word-wrap:normal; word-break:normal">Author Entity</span>包含两个<span lang="EN-US" style="word-wrap:normal; word-break:normal">Attribute :<wbr style="word-wrap:break-word">name</wbr></span>和<span lang="EN-US" style="word-wrap:normal; word-break:normal">email</span>,它们对于的运行时类均为<span lang="EN-US" style="word-wrap:normal; word-break:normal">NSManagedObject</span>;还包含一个与<span lang="EN-US" style="word-wrap:normal; word-break:normal">Post</span>的<span lang="EN-US" style="word-wrap:normal; word-break:normal">Relationship</span>;没有设置<span lang="EN-US" style="word-wrap:normal; word-break:normal">Feteched Property</span>访问属性。</wbr></wbr>

我们通常使用KVC机制来访问Property。下面来看代码:

[代码]c#/cpp/oc代码:

1 NSManagedObjectContext * context = [[NSAppdelegate] managedObjectContext];
2 NSManagedObject * author = nil;
3
4 author = [NSEntityDescription insertNewObjectForEntityForName:@"Author"inManagedObjectContext: context];
5 [author setValue:@"nemo@pixar.com"forKey:@"email"];
6
7 NSLog (@"The Author's email is: %@", [author valueForKey:@"email"]);

在上面代码中,我们先取得NSManagedObjectContext,然后调用NSEntityDescription的方法,以Author为实体模型,生成对应的NSManagedObject对象,插入NSManagedObjectContext中,然后给这个对象设置特性email的值。

<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><span style="word-wrap:normal; word-break:normal; line-height:33px; font-size:22px">五、运行时——类与对象</span></wbr></wbr></wbr>

> Managed Object– NSManagedObject

Managed Object表示数据文件中的一条记录,每一个Managed Object在内存中对应Entity的一个数据表示。Managed Object成员EntityProperty所描述。

比如在上面的代码,author这个NSManagedObject,对应名为AuthorEntity

<wbr style="word-wrap:break-word"></wbr>

每一个Managed Object都有一个全局ID(类型为:NSManagedObjectID)。Managed Object会附加到一个Managed Object Context,我们可以通过这个全局IDManaged Object Context查询对应的Managed Object

NSManagedObject常用方法

-entity

获取其Entity

-objectID

获取其Managed Object ID

-valueForKey:

获取指定Property的值

-setValue: forKey:

设定指定Property的值

> Managed Object Context– NSManagedObjectContext

Managed Object Context作用相当重要,对数据对象进行的操作都与它有关。当创建一个数据对象并插入Managed Object Context中,Managed Object Context就开始跟踪这个数据对象的一切变动,并在合适的时候提供对undo/redo的支持,或调用Persistent Store Coordinato将变化保存到数据文件中去。

通常我们将controller类(如:NSArrayControllerNSTreeController)或其子类Managed Object Context绑定,(如何绑定?后有介绍)这样就方便我们动态地生成,获取数据对象等。

NSManagedObjectContext常用方法

-save:

将数据对象保存到数据文件

-objectWithID:

查询指定Managed Object ID的数据对象

-deleteObject:

将一个数据对象标记为删除,但是要等到Context提交更改时才真正删除数据对象

-undo

回滚最后一步操作,这是对undo/redo的支持

-lock

加锁,常用于多线程以及创建事务。同类接口还有:-unlock and -tryLock

-rollback

还原数据文件内容

-reset

清除缓存的Managed Objects。只应当在添加或删除Persistent Stores时使用

-undoManager

返回当前Context所使用的NSUndoManager

-assignObject: toPersistantStore:

由于Context可以管理从不同数据文件而来的数据对象,这个接口的作用就是指定数据对象的存储数据文件(通过指定PersistantStore实现)

-executeFetchRequest: error:

执行Fetch Request并返回所有匹配的数据对象

<wbr style="word-wrap:break-word"></wbr>

> Persistent Store Coordinator- NSPersistentStoreCoordin<wbr style="word-wrap:break-word">ator</wbr>

使用Core Data document类型的应用程序,通常会从磁盘上的数据文中中读取或存储数据,这写底层的读写就由Persistent Store Coordinator来处理。一般我们无需与它直接打交道来读写文件,Managed Object Context在背后已经为我们调用Persistent Store Coordinator做了这部分工作。

NSPersistentStoreCoordin<wbr style="word-wrap:break-word">ator</wbr>常用方法

-addPersistentStoreForURL<wbr style="word-wrap:break-word">:configuration:URL:options:error:</wbr>

装载数据存储,对应的卸载数据存储的接口为-removePersistentStore:error:

-migratePersistentStore:toURL:options:withType:error:

迁移数据存储,效果与"save as"相似,但是操作成功后,迁移前的数据存储不可再使用

-managedObjectIDForURIRep<wbr style="word-wrap:break-word">resentation:</wbr>

返回给定URL所指示的数据存储的object id,如果找不到匹配的数据存储则返回nil

-persistentStoreForURL:

返回指定路径的Persistent Store

-URLForPersistentStore:

返回指定Persistent Store的存储路径

<wbr style="word-wrap:break-word"></wbr>

> Persistent Document- NSPersistentDocument

NSPersistentDocumentNSDocument子类multi-document Core Data应用程序使用它来简化对Core Data的操作。通常使用NSPersistentDocument的默认实现就足够了,它从Info.plist中读取Document types信息来决定数据的存储格式(xml,sqlite, binary)。

NSPersistentDocument常用方法

-managedObjectContext

返回文档的Managed Object Context,在多文档应用程序中,每个文档都有自己的Context

-managedObjectModel

返回文档的Managed Object Model

<wbr style="word-wrap:break-word"></wbr>

<wbr style="word-wrap:break-word"><wbr style="word-wrap:break-word"><span style="word-wrap:break-word">六、<span lang="EN-US" style="word-wrap:normal; word-break:normal">Fetch Requests</span></span></wbr></wbr>

Fetch Requests相当于一个查询语句,你必须指定要查询的Entity。我们通过Fetch RequestsManaged Object Context查询符合条件的数据对象,以NSArray形式返回查询结果,如果我们没有设置任何查询条件,则返回该Entity的所有数据对象。我们可以使用谓词来设置查询条件,通常会将常用的Fetch Requests保存到dictionary以重复利用。

NSFetchRequest常用方法

-setEntity:

设置你要查询的数据对象的类型(Entity

-setPredicate:

设置查询条件

-setFetchLimit:

设置最大查询对象数目

-setSortDescriptors:

设置查询结果的排序方法

-setAffectedStores:

设置可以在哪些数据存储中查询


[代码]c#/cpp/oc代码:

01 NSManagedObjectContext * context = [[NSAppdelegate] managedObjectContext];
02 NSManagedObjectModel * model = [[NSAppdelegate] managedObjectModel];
03 NSDictionary * entities = [model entitiesByName];
04 NSEntityDescription * entity = [entities valueForKey:@"Post"];
05
06 NSPredicate * predicate;
07 predicate = [NSPredicate predicateWithFormat:@"creationDate > %@", date];
08
09 NSSortDescriptor * sort = [[NSortDescriptor alloc] initWithKey:@"title"];
10 NSArray * sortDescriptors = [NSArray arrayWithObject: sort];
11
12 NSFetchRequest * fetch = [[NSFetchRequest alloc] init];
13 [fetch setEntity: entity];//设置你要查询的数据对象的类型(Entity)
14 [fetch setPredicate: predicate];//设置查询条件
15 [fetch setSortDescriptors: sortDescriptors];// 设置查询结果的排序方法
16
17 NSArray * results = [context executeFetchRequest:fetch error:nil];
18 [sort release];
19 [fetch release];

在上面代码中,我们查询在指定日期之后创建的 post,并将查询结果按照 title 排序返回。

参考资料:

Core Data Reference API listing for the Core Data classes

http://developer.apple.com/documentation/Cocoa/Reference/CoreData_ObjC/index.html

NSPredicate Reference API listing for NSPredicate

http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSPredicate.html


原贴地址:http://www.devdiv.com/iPhone开发学习笔记——Core_Data_框架及运作过程和设计的类-weblog-228548-49362.html

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics