本小节主要讲述Core Data相关方面的知识,以及如何使用。
Core Data基础知识
官方的说法是:Core Data is a schema-driven object graph management and persistence framework.
翻译过来的意思大概是:Core Data是一个模式驱动的对象图管理和持久化框架。
好吧,上面的字面意思不是很容易理解,那么我们从以下几个方面来帮助那些有其余开发经验的程序员树立一些观念:
- Core Data不是一个数据库,但是他可能使用一个数据库。默认情况下,Core Data将使用SQLite,但是你可以完全不用关心任何关系型数据库相关知识,或者SQL语句;
- Core Data不是一个类似于Hibernate的对象关系映射框架。Core Data仅仅是一个对象间的关系模型,用户无需数据库知识,并且数据也不一定保存在数据库中;
- Core Data允许你去保存模型对象到文件中并且可以在需要的时候取回数据,这一系列操作类似于打包和序列化操作,但是Core Data能够做的更多;
Core Data能够做的比较强大的功能包括:
- Core Data提供了维护模型对象变化的基础能力,这样你可以执行撤销和重做操作,并且Core Data可以维护对象间的相互关系;
- Core Data允许你在给定时间内在内存中保留模型对象的一个子集,这对于严苛的iOS内存环境相当重要;
- 使用Schema来描述模型对象。你可以在一个图形化的编辑器中定义你的模型的主要特性,甚至可以包含各个对象间的关系。这样提供了一个简单的手段来保证一个基本的健壮性,例如可以设置缺省值和属性值的验证;
- 允许你维护对象的不想交集合。例如,允许你在一个视图中对对象进行编辑,但这个内容可能被丢弃,导致另一个视图中不更新对象;
- 对对象的版本管理操作提供支持,这样可以很方便的将旧版对象升级为新版对象;
Core Data不是一个单独的类,而是一群需要互相协作的类的集合。整个Core Data的架构图是比较复杂的:
实例
MyCoreData
- 建立一个新的Empty工程,名字叫MyCoreData,工程属性选择Core Data和ARC支持;
- 在继续下一步之前,我们了解几个内容:
- Managed Object Model,描述了你将在App中使用的Schema。如果你有数据库背景知识,你可以想象一下数据库的Schema。这里的Schema实际上指的是一个对象(或者说实体)的集合。在XCode中,模型被保存在.xcdatamodeld文件中。你可以使用可视化的编辑器来定义实体、属性、关系等;
- Persistent Store Coordinator,在iOS中默认使用SQLite存储持久化数据,当然Core Data提供了存储不同实体到不同存储对象的手段。PSC就是对存储手段进行维护的管理器。当然你完全可以忘记这个对象,因为你基本上不需要和他直接交互;
- Managed Object Context,这个对象用于管理对象的序列化和反序列化工作,也是在使用Core Data Stack时主要使用的工具;
- 整理下可以发现,从底层往上的顺序分别是:DataStore->Persistent Object Store->Persistent Store Coordinator->Managed Object Context->App;
- 基本概念讲完了,我们点击项目生成的.xcdatamodel文件,点击Add Entity,添加一个实体,名字叫Novel;
- 添加属性,定义如下的三个属性值:
- 创建用户界面,在File->New中创建一个StoryBoard,然后在Project属性中设置为这个Story Board;
- 在AppDelegate.m文件中,将自动生成的didFinishLaunchingWithOptions方法的实现清空,变为:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
return YES;
}
- 在Story Board编辑界面中,添加一个Table View Controller、一个View Controller,设置Table View Controller跳转到View Controller(注意选择Segue的Modal,因为后面使用dismissViewController来释放Details窗口),并且Embed in到Navigator View Controller;
- 在Table View Controller中添加一个Button,并且选择Button的Identifier为Add,使之变成一个加号按钮;
- 选择Table View的Cell,将其Style修改为Right Detail;
- 去那个名为Details的View Controller,先放置一个Navigation Bar到界面上面,然后拖动两个Button到Navigation Bar上面,并且分别设置对应的Bar Button Item的Identifier属性为Cancel和Save,如下:
- 在Details界面中添加三个text field,并且分别设置他们的placeholder属性为:name、author、version;
- ListView上面的那个加号按钮在点击时需要跳转到Details界面,所以需要在Story Board上选择这个按钮,按住Control键,拖动到Details这个View之上后释放鼠标,选择Segue->Modal;
- 在源代码文件夹中创建一个Object-C类,命名为NovelListViewController,父类选择UITableViewController;
- 在Story Board中,将Table View的Class设置为NovelListViewController;
- 同样,设置Details这个View的Class为新建立的NovelDetailsViewController;
- 在NovelDetailsViewController中生成和界面中的TextField对应的控件变量及响应方法:
#import <UIKit/UIKit.h>
@interface NovelDetailsViewController : UIViewController
@property (strong, nonatomic) IBOutlet UITextField *tfName;
@property (strong, nonatomic) IBOutlet UITextField *tfAuthor;
@property (strong, nonatomic) IBOutlet UITextField *tfVersion;
- (IBAction)cancel:(id)sender;
- (IBAction)save:(id)sender;
@end
- 接下来进入正题,我们将进入操作Core Data的阶段。我们将要实现的主要功能包括:在Details页面中保存数据,在List页面中将数据取回;
- 在NovelDetailsViewController.m中加入代码:
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:@selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- 记得我们当时对工程添加Core Data支持的时候,我们的AppDelegate中自动添加了一个Managed Object Context,我们将使用这个Context来保存或者取回对象;
- 我们将两个按钮的事件处理方法变为:
- (IBAction)cancel:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)save:(id)sender {
// get Managed Object Context
NSManagedObjectContext *context = [self managedObjectContext];
// Create a new managed object
NSManagedObject *newNovel = [NSEntityDescription insertNewObjectForEntityForName:@"Novel" inManagedObjectContext:context];
[newNovel setValue:self.tfName.text forKey:@"name"];
[newNovel setValue:self.tfAuthor.text forKey:@"author"];
[newNovel setValue:self.tfVersion.text forKey:@"version"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
- 当点击Cancel按钮的时候,我们使用一个动画方式将Details窗口关闭掉。
当点击Save按钮的时候,我们首先获取Context,然后创建一个新的Managed Object,设置novel的属性,接着调用context的save来存储数据,最后关闭界面。 - 现在我们看如何获取列表。我们在TableViewController中添加一个数组用来保存Novel的列表:
#import <UIKit/UIKit.h>
@interface NovelListViewController : UITableViewController
@property(strong, nonatomic) NSMutableArray *novelList;
@end
- 同样对于List页面需要添加获取Context和加载时获取数组元素列表接口:
@implementation NovelListViewController
@synthesize novelList;
- (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:@selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Novel"];
self.novelList = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[self.tableView reloadData];
}
- 接下来改造List界面的表格显示模块:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#warning Incomplete method implementation.
// Return the number of rows in the section.
return self.novelList.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
NSManagedObject *device = [self.novelList objectAtIndex:indexPath.row];
[cell.textLabel setText:[NSString stringWithFormat:@"%@ %@", [device valueForKey:@"name"], [device valueForKey:@"version"]]];
[cell.detailTextLabel setText:[device valueForKey:@"author"]];
return cell;
}
- 注意在Story Board中设置Table View Cell的Identifier属性值为Cell,与代码中的保持一致。
- 至此,程序功能全部完成,查看发现数据能够正常保存,但我们没有写一行和数据库相关的代码。