Core Data. 单元目标 Core Data 简介 数据模型映射 Core Data 数据访问...

41
Core Data

Transcript of Core Data. 单元目标 Core Data 简介 数据模型映射 Core Data 数据访问...

Core Data

单元目标

• Core Data 简介• 数据模型映射• Core Data 数据访问• 生成实体类代码• Core Data 查询

22

Core Data 简介

33

Core Data 简介• Core Data 是 Cocoa 里面一套非常受欢迎的

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

44

Core Data 简介• 面向对象的程序员大多都不喜欢用基于 C API

去做数据持久化。• 希望使用面向对象的编程去持久化数据。• 使用 Core Data 是比较好的选择。• 它是支持数据库方式,建立对象图。

– 通常使用 SQLite3

• 它是如何工作的?– 通过可视化的方式建立数据库和对象的映射– 使用面向对象的 API 来查询对象– 使用对象属性访问数据库中对应表的列

55

数据模型映射

如何创建数据模型映射• 在 Xcode 里 , Resource 目录下新建文件

并选择 Data Model

77

如何创建数据模型映射• 可以用已存在的类和它们的属性当作模板

– 但很少这样做,通常从头开始生成对象并映射– 可以跳过这个窗口

88

.xcdatamodel 文件创建• 这个文件用于建立对象和数据库的映射,

和 .xib 文件类似。

99

创建实体• 在数据库中实体对应为“表“,在我们的

代码里映射为”对象”。– 这里 用 Photo 实体来代表照片

1010

点击 +号 , 添加一个实体

修改实体名为 Photo

对应实体类型NSManageObject

给实体添加属性• 属性映射为数据库的“列”,映射为对象属性• thumbnailURL 用于存储 URL, 使用它下载我们的照片缩略

1111

点击+ 号来添加属性

和修改实体名一样

属性的类型为” string”. 对应到代码里为NSString

属性选项•

1212

Optional: 对象创建时默认值应许为空

Transient: 只有在对象端有这个属性(不存到数据库中)

Indexed: 为了提高属性搜索效率

增加另一个属性• 增加 thumbnailData 属性,用于存储照片缩略图

实际的图片数据

1313

创建另外一个实体• Photograhper 实体代表谁照了这张照片

1414

建立两个实体间的关联关系• 建立一个”一对多”关联关系 photos

– 在对象中映射为 (NSSet *)– 一个摄像师拥有很多照片

1515

通过” Add Relationship”来增加关联关系

关联关系名称

指向 Photo 对象它没有” inverse”关系,它们是“ To-Many”的关联关系

双向关联 (inverse 关联关系 )• 在 Photo 实体上,增加 whoTook 关联关系,它指向

Photographer 实体。• whoTook 不是多对一关系,因此它是 NSManagedObject 属性

(而不是 NSSet )

1616

whoTook 是Photos 的逆向关联关系

箭头变成双向

Core Data 数据访问

Core Data 数据访问• 关于 Core Data 还有许多其它事情可以做

– 前面部分我们只关心创建实体,属性,关联关系• 如何通过程序方式访问数据?– 需要用 NSManagedObjectContext

• 如何得到 NSManagedObjectContext– 在创建项目时,选择复选框“ User Core Data for storage”

– 应用程序 代理 里有一些代码支持 Core Data– 最特别的是代理中有一个属性” managedObjectContext”– 正是利用这个对象去访问数据库中,创建或查询对象

1818

如何创建一个新对象

1919

• NSEntityDescription 类方法返回一个 NSManagedObject.• 所有来自数据库的对象不是 NSManagedObject 就是它的子类• 通过传递参数方式来引用 NSManagedObjectContext

– 上面的代码通常不是在应用程序代理中– 不要把应用程序代理当成全局变量– 所以必须在 API 中传递 NSManagedObjectContext(Controllers 等 )– 通常,在视图控制器如下方式处理 Core Data 的显示

• initInManagedContext: (NSManagedObjectContext *) Context

NSManagedObject *photo = [NSEntityDescription insertNewObejectForEntityForName:@”Photo” inManagedObjectContext: (NSManagedObjectContext *) ctxt];

如何获得属性值• 可以通过以下两个方法来访问

– ( id ) valueForKey: (NSString *)key;– (void) setValue: (id) value forKey:(NSString *)key;

• key 是对象映射中的属性名– 如: @”thumbnailURL”

• value 将被存储到数据库里– 如果值没有被设置,默认是 nil 。– 所有的值都是对象– Binary data 值对应的是 NSData 对象– “To-many” 映射的关联关系值对应为 NSSet 对象• 一切都会为你管理• SQL 自动生成,数据延迟加载等

2020

如何保存数据• 数据修改 ( 写入 ) 都是花生在内存中,除非你 保存

– 任何时候一批数据变化,都应保存– Save: 是 NSManagedObjectContext 的实例方法– 保存之前,需要判断对象是否有改变

• - ( BOOL ) hasChanges;

• 典型的代码块

– 所有错误都可以通过 NSError userInfo 来报告

2121

-( void) saveChangesToObjectsInMyMOC: (NSManagedObjectContext *) context { NSError *error = nil; if ( [context hasChanges] && ![context save:&error]){ NSLog(@”错误! %@”,error,[error userInfo] ); abort();// 在这里生成崩溃 log,用于调试 }

生成实体类代码

两种 Xcode 生成代码的方式• 用我们创建的数据映射描述来创建新的实

体类• “Copy and paste” 方式

2323

映射描述来创建新的实体类• 在 Xcode 选择需要生成代码的实体

2424

选择新建文件• 选择 “ Managed Object Class” 作为要创建

文件的类型

2525

选择存放生成类的项目目录• 只要点击” Next”按钮

2626

选择实体生成类•

2727

对应生成的文件• 在 resource 目录下创建了两个文件 (.h,.m)

– 为了目录不混乱,最好移到 Classes 目录下

2828

“Copy and paste” 方式• 在以下两种情况下使用

– 如果已经有 NSManagedObject定制子类文件– 或许想添加或改变一个属性并且需要代码生成

属性

2929

3030

选择需要生成代码的属性

Copy 和 paste 生成的代码到 .h 或 .m

生成的代码 .h

• 只有每一个实体属性对应的 @property

3131

#import <CoreData/CoreData.h>@interface Photo : NSManagedObject {}@property (nonatomic, retain) NSData * thumbnailData;@property (nonatomic, retain) NSString * thumbnailURL;@property (nonatomic, retain) NSManagedObject * whoTook;@end

#import <CoreData/CoreData.h>@class Photo;@interface Photographer : NSManagedObject {}@property (nonatomic, retain) NSSet* photos;@end

生成的代码 .m

•@dynamic – 使用运行期机制拦截对象消息,在这种情况

下, NSManagedObject 使用 valueForKey: 或setValueForKey: 来操作属性

– 使用 . 记号来访问数据库实体

3232

@implementation Photo @dynamic thumbnailData;@dynamic thumbnailURL;@dynamic whoTook;@end

Photo *photo = [NSEntityDescription insertNewObjectForEntityForName:@”Photo” inM..]NSString * myThumbnail = photo.thumbnailURL;Photo.thumbnailData = …Photo.whoTook = …/ 一个摄像师对象,可以通过查询语句创建

NSFetchRequest• 到目前为止,我们可以

– 通过 insertNewObjectForEntityForName:inManagedobjectContext: 来创建持久化对象

– 通过 valueForKey:/setValueForKey 或 @property 来 访问或改变属性值

• 在 Core Data 框架里如何查询数据– 可以通过执行 NSManagedObjectContext 中的 NSFetchRequest 来

获得查询数据• 查询过程中涉及到的四个重要对象– NSEntityDescription 指定查询那个实体(必须)– NSPredicate 指定获取那些实体对象(可选,默认获得所有对象)– NSSortDescription 指定返回数组的排序方式。(可选,随机的)– 指定一次获取多少对象或最大获取多少个对象。(可选,所有对

象)

3333

创建 NSFetchRequest

• 查询代码块

• 通过实体描述来获取想要的对象– request.entity = [NSEntityDescription entityForName:@”Photo” inManagedObjectContext:ctxt];

– 通过 NSEntityDescription 来创建实体描述的实例

– 设置获取数据的大小或最大数

3434

NSFetchRequest *request = [[NSFetchRequest alloc ] init];request.entity = [NSEntityDescription entityForName:@”Photo” inManagedObjectContext:ctxt];request.fetchBatchSize = 20;request.fetchLimit = 100;Request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];Request.predictate = ….;

NSSortDescriptor• 当执行 fetchRequst 对象时,它将返回一个存

NSManagedObject 对象数组。• 数组是“有序的”,因此我们获取数据时通常想

指定顺序。通过给 fetchRequest 一列 “排序描述”用它来描述什么排序方式

• 也可以不用 selector 参数(默认使用的方法 compare: )

• selector: 参数只是一个方法,它可以对每个关键字进行相互比较。

3535

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”thumbnailURL” ascending: YES selector: (localizedCaseinsensitiveCompare:)];

NSPredicate

• 如何准确指定那些数据是我们想要从数据库中获取的呢?

• predicateWithForm:arguments….– @”uniqueid == %@,[dept objectForKey:@”id”]– “@ in tags”,(NSMagagedObject *)// tags 是 to-many 的关

系– “viewed > %@”,(NSDate *)// viewed 是日期类型的属性– @”name contains[c] %@”,(NSString *)//匹配 name 属性的字符串,大小些敏感

3636

NSString *username =@”davinlei”;NSPredicate *predicate = [NSPredicate predicateWithFormat: @”name contains %@”, username];

NSCompoundPredicate

• 用并且和或的运算方式组合 predicate条件

3737

NSArray * array = [NSArray arrayWithObjects:predicate1,predicate2,nil];NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:array];

综合使用• Photographer 是 NSManagedObject 的子类• 通过 photographerWithName 方法从数据库获取

一个 photographer

• 创建 fechRequest 对象 获取 davinlei 所拍的所有照片

3838

NSManagedObject *photographer = [Photographer photographerWithName:@”davinlei” inManagedObjectContext]

NSFetchRequest *request = [[NSFethcRequest alloc] init];request.entity = [NSEntityDescription entityForName:@”Photo” inManagedObjectContext:ctxt];request.fetchBatchSize = 20;request.soreDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@”title” ascending: YES]; request.predicate = [NSPredicate predicateWithFormat:@”whoTook = %@”,photographer]

fetchRequst 实际获得的结果• 使用 executeFetchRequest: error 方法

– 如果发生异常,则返回 nil(详细错误信息查看 NSErro)– 如果没有任何匹配的对象,则返回空数组– 如果有数据匹配,则返回 NSManagedObjectd 对象数组– 如果不关心错误信息,可以传 NULL 给 error 参数• 遍历结果

3939

NSFetchRequest *request = ……NSError **error = nil;NSArray *results = [managedObjectContext executeFetchRequest:request error:&error];

NSManagedObject *photo = [results objectAtIndex:0];For(Photo *photo in results ){….}Photo *photo = [results lastObject];//返回 0 或 1 个对象

删除对象

• 如果对象之间有关联关系?– 如: 当删除 photograther拍的最后一张照片,是否删

除 photograther?– 可以在关联控制地方设置

• 选中“ Createing and Deleting Managed Objects”

4040

[managedObjectContext deleteObject (NSManagedObject *) anObject];

Question?

4141