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还能对非法数据进行过滤。更重要的是,Core Data的NSFetchRequest
类可以替代SQL中的Select语句,并提供了NSFetchedResultsController以更高效的方法将查询结果显示在UITableView中。
Core Data 组件
Core Data的组件主要由三部分组成:
Managed Object Model(数据模型): 可以看作是数据库的模型结构。包含了各个实体的定义信息。
Persistent Store Coordinator (持久化存储协调器):将对象图管理部分和持久化部分捆绑在一起,当它们两者中的任何一部分需要和另一部分交流时,这便需要持久化存储协调器来调节。
Managed Object Context (管理数据上下文):被管理数据的上下文,实际上是对你所有数据库操作的一个缓存层,会把你所有的操作都先缓存起来避免大量磁盘 IO 造成不流畅,你在操作完数据库后调用其save方法持久化改变。
上图是Core Data 基本工作原理 可以帮助我们更加深入的了解Core data各个组件间是怎么工作的 下面通过一个例子详细介绍下core data究竟干了什么
一个Core Data 工程
在这里我们不需要自己动手去做一个工程 因为xcode已经 为我们提供了一个完整的Core Data 项目 打开我们的xcode 在 iOS -> Application 下选择 Master-Detail Application 命完名字后记得勾选Core Data选项 项目建立后我们可以发现Frameworks中已经有了CoreData.framework
一项,并且还多了一个以 .xcdatamodeld结尾的文件,这个 文件定义了我们需要 的数据模型结构。点开后 可以看到左侧有三个选项:Entities,Fetch Request、Configurations。
Entities
Entities Core Data 的一个实体 它可以像类一样被继承 为了便于理解我们也可以把它理解为数据库中的一个表,实体里的属性可以看做数据库表中的属性 我们的项目中现在有一个Event实体,这个实体有一个叫timeStamp的属性 这里可以理解为,我们的数据库中有一个叫Event的表 表里有一个叫timeStamp的属性
Attributes 就是我们上面说的属性,我们可以设置其数据类型,默认值,最大,小值等。需要注意的是这里的空值是NULL,不等同于OC中的nil,更不等同于0和空字符串@“”。
Relationships 描述多个Entities间的关系:多对一,一对一,继承关系等。当我们指定了一个关系后,我们也最好指定一个反转关系。比如A和B是多对多的关系,那么A指向B的关系Type为To Many,同时设定B指向A的关系Type为To Many,如果A为B的父类那么同时要指定B为A的子类。
Fetched Property表示了一种弱的、单向的关系。Core Data不支持在persistent store之间建立Relationships,所以Fetched Property可用于建立“松耦合”关系,相似暂时的分组。
Fetch Request
在Core Data中我们使用NSFetchRequest
类来进行数据请求,从持久存储(persistent store)中获取对象。请求中包含变量(如查找条件)类似我们sql语句中的查找条件,这个我们以后有机会在介绍。
Configurations
配置包含了一个名称和若干个相关的实体。一个实体可以出现在多个配置中。我们可以在代码中使用setEntities: forConfiguration:
的方法来指定配置。我们也可以使用entitiesForConfiguration:来获取配置
。一般说来,如果你想把不同的实体存放在不同的存储中去,就可能用到配置。一个持久化存储协调器(persistent store coordinator)只能有一个被管理的对象模型(managed object model)。所以,默认情况下,和协调器关联的某个存储必须包含同样的实体。要想绕过这个限制,你可以创建一个包含所有实体并集的模型,然后在模型中为每一个你想使用的实体子集创建配置,这样一来,使用这个模型创建协调器,当你需要添加存储(persistent store)时,可根据不同的配置来指定对应的存储属性。
通过上面的简单介绍我们已经对Core Data有了初步的了解 下面我们准备从苹果的模板代码体会Core Data是如何具体应用的
首先是AppDelegate.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h> @interface AppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; - (void)saveContext;
- (NSURL *)applicationDocumentsDirectory; @end
这里比我们平时创建的项目多了三个属性 就是我们上面介绍过的Core Data 的三个组件 managedObjectContext managedObjectModel persistentStoreCoordinator
下面移步.m文件 了解下三个部件的实例化过程
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
} NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
如果是第一次调用,会实例化一个NSManagedObjectContext
对象,并使用persistentStoreCoordinator
方法返回的NSPersistentStoreCoordinator
对象来配置上下文,最后返回新实例化的NSManagedObjectContext
对象。
然后来看下persistentStoreCoordinator的实例化
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it.
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
} // Create the coordinator and store _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"ZYCoreData.sqlite"];
NSError *error = nil;
NSString *failureReason = @"There was an error creating or loading the application's saved data.";
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
// Report any error we got.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
dict[NSLocalizedFailureReasonErrorKey] = failureReason;
dict[NSUnderlyingErrorKey] = error;
error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code: userInfo:dict];
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
} return _persistentStoreCoordinator;
}
同样的如果是第一次调用会实例化一个NSPersistentStoreCoordinator对象,这里要访问到documents目录中的SQLite存储文件ZYCoreData.sqlite,还定义了一个applicationDocumentsDirectory
方法,它的作用是获取程序documents的路径
- (NSURL *)applicationDocumentsDirectory {
// The directory the application uses to store the Core Data store file. This code uses a directory named "com.maxjia.ZYCoreData" in the application's documents directory.
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
ZYCoreData.sqlite文件是在编译ZYCoreData.xcdatamodeld时生成的。 NSPersistentStoreCoordinator
初始化时需要传入managedObjectModel
。NSPersistentStoreCoordinator
对象在添加持久存储的时候不仅需要传入存储类型,存储文件URL,选项以及错误类型。如果添加存储的时候出现错误,就会进入if判断,进行错误处理。
然后是managedObjectModel
- (NSManagedObjectModel *)managedObjectModel {
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"ZYCoreData" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
NSManagedObjectModel
类在初始化的时候用到了ZYCoreData.momd文件,这个文件和上面ZYCoreData.sqlite一样是编译项目时,由ZYCoreData.xcdatamodeld数据模型生成,并且保存到app的Bundle目录
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
[self saveContext];
}
- (void)saveContext {
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
NSError *error = nil;
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
managedObjectContext
对象中的数据的修改,发生在内存中的,需要调用save
方法来保存到存储文件当中才能做到数据的持久化,在这里选择在程序退出时调用saveContext保存数据
部件介绍完了 看看我们这个程序干了什么。首先在程序初始化的时候
UINavigationController *masterNavigationController = splitViewController.viewControllers[];
MasterViewController *controller = (MasterViewController *)masterNavigationController.topViewController;
controller.managedObjectContext = self.managedObjectContext;
return YES;
给MasterViewController中
传入了managedObjectContext MasterViewController继承自
UITableViewController 所以我们运行程序 可以看到这个程序实现了 点击左上角加号 把当前时间标签加入到tableView中的功能 多添加几条并关闭程序从新打开 发现之前的数据仍然存在 说明应用做了数据的持久化处理 下面我们看下实现代码
首先是UITableViewDataSource的相关代码
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count];
} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];
return cell;
} - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
} - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]]; NSError *error = nil;
if (![context save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
} - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[object valueForKey:@"timeStamp"] description];
}
通过代码 我们发现就可看出tableView的数据是由fetchedResultsController
对象提供的。所以我们继续来看看fetchedResultsController是何方神圣
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
} NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity]; // Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:]; // Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO]; [fetchRequest setSortDescriptors:@[sortDescriptor]]; // Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController; NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
} return _fetchedResultsController;
}
Core Data在iOS上使用了NSFetchedResultsController
对象来简化对提取结果和表格视图的处理。NSFetchedResultsController
对象被惰性创建并只在表格视图数据源方法有需要时才提取数据。我们可以看到在对NSFetchRequest
对象的处理中,使用了Event实体,并提供了一个NSSortDescriptor
对象以让提取结果按timeStamp进行排序。最后通过NSFetchRequest
对象和managedObjectContext
作为参数传入NSFetchedResultsController
的初始化方法。NSFetchedResultsController
也有它的代理,将MasterViewController
设置为其代理,这样在fetched results 发生变化时,MasterViewController
中实现的NSFetchedResultsControllerDelegate
方法会被调用,然后就可以实现在数据变化是改变tableView中的显示内容
上面是用NSFetchedResultsController批量获取数据的方法 下面看下数据插入的操作
- (void)insertNewObject:(id)sender {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context]; // If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
[newManagedObject setValue:[NSDate date] forKey:@"timeStamp"]; // Save the context.
NSError *error = nil;
if (![context save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
插入数据需要调用实体描述对象NSEntityDescription返回一个实体对象,然后设置对象属性,最后保存当前上下文即可。这里需要注意,增、删、改操作完最后必须调用管理对象上下文的保存方法,也就是这里[context save:&error]方法
,否则不能被执行。
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]]; NSError *error = nil;
if (![context save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
在tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath实现了删除操作 拿到上下文 直接调用管理对象上下文的deleteObject方法,删除完保存上下文即可
总结
Core Data框架基本的5个类:NSPersistentStoreCoordinator、NSManagedObjectContext、NSManagedObjectModel、NSEntityDescription、NSManagedObject。
NSPersistentStoreCoordinator持久化存储协调器:负责从文件加载数据和将数据写入文件。
NSEntityDescription实体描述:实体可以被看做是NSManagedObject对象的一个具体的实现。
NSManagedObjectContext 管理对象上下文:上下文是内存中的一块暂存区域。查询,插入,删除,修改等操作都是在上下文中进行。在上下文没有保存之前,对数据的任何修改都只记录在暂存区中,不会影响文件内的数据。
NSManagedObject 管理对象:Core Data的核心单元。模型对象的数据被持有在NSManagedObject对象中。每一个NSManagedObject对象都对应一个实体
NSManagedObjectModel 对象模型:NSManagedObjectModel通常被定义在一个.mom文件中,文件中保存了所有实体的定义。
ios开发:Core Data概述的更多相关文章
- 《驾驭Core Data》 第一章 Core Data概述
<驾驭Core Data>系列教程综合了<Core Data for iOS>,<Learning Core Data for iOS>,<Core Data ...
- #译# Core Data概述 (转)
昨晚熬夜看发布会(本以为屌丝终于能买得起苹果了,谁知道...),因为看不了视频直播,所以就正好有空就把www.objc.io最新的一篇文章翻译了一下,同时感谢CocoaChina翻译组提供校对,以下为 ...
- iOS-Core Data 详解
使用Core Data 框架 Core Data框架本质就是一个ORM(对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一 ...
- iOS之Core Data及其线程安全
一.简介 Core Data是iOS5之后才出现的一个框架,它提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite数据库文件中,也能够将保存在数据库中的数据还原成OC对 ...
- Core Data 概述
Core Data是一个模型层的技术.Core Data帮助你建立代表程序状态的模型层.Core Data也是一种持久化技术,它能将模型对象的状态持久化到磁盘,但它最重要的特点是:Core Data不 ...
- IOS开发——Core Graphics & Core Animation
好久没写过blog了.首先了解下近期苹果和IOS方面的最新消息. 1.WWDC2014在上个月举行了,与2013年一样.今年WWDC没公布硬件产品和新品(假设你懂cook你就会期待今年秋季公布会.估计 ...
- Core Data概述(转)
Core Data是一个模型层的技术.Core Data帮助你建立代表程序状态的模型层.Core Data也是一种持久化技术,它能将模型对象的状态持久化到磁盘,但它最重要的特点是:Core Data不 ...
- iOS开发 - Core Animation 核心动画
Core Animation Core Animation.中文翻译为核心动画,它是一组很强大的动画处理API,使用它能做出很炫丽的动画效果.并且往往是事半功倍. 也就是说,使用少量的代码就能够实现很 ...
- iOS开发-Core Location和Map Kit
一.Core Location确定物理位置 利用以下3种技术: 1.GPS(最精确的) 2.蜂窝基站ID定位(cell ID Location) 3.WPS(Wi-Fi Positioning Ser ...
随机推荐
- cdoj 31 饭卡(card) 01背包
饭卡(card) Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/31 Des ...
- 搭建Spring + SpringMVC + Mybatis框架之二(整合Spring和Mybatis)
整合Spring和Mybatis 首先给出完整的项目目录: (1)引入项目需要的jar包 使用http://maven.apache.org作为中央仓库即可. Spring核心包,mybatis核心包 ...
- 初识ASP.NET---若干常见错误
近期在学习ASP.NET的相关知识,期间遇到了一些错误,比較常见的错误总结了一下,希望此文能给ASP.NET刚開始学习的人一些帮助.同一时候记录这些错误也方便今后自己查看. 1. GridView& ...
- java最简单的方式实现httpget和httppost请求
java实现httpget和httppost请求的方式多种多样,个人总结了一种最简单的方式,仅仅需几行代码,就能够完美的实现. 此处须要用到两个jar包,httpclient-4.3.1.jar.ht ...
- Android实现Filterable通过输入文本框实现联系人自动筛选
相信大家一定在见过手机通讯录的一个情景就是使用在选人的时候输入文本框里的数据就能自动筛选.实现的效果如下图. 其实实现这样的效果相信大家一定对另外一个控件不陌生那就AutoCompleteTextvi ...
- OpenRisc-67-OR的汇编
引言 之前我们写过OR的裸机程序,写过基于OR的linux设备驱动程序,也反汇编过OR的机器码. 本小节,我们将通过一个简单的实验,对OR的汇编(指令集)做一个简单的梳理和測试. 1,基本思想 要想了 ...
- 文件系统缓存dirty_ratio与dirty_background_ratio两个参数区别
这两天在调优数据库性能的过程中需要降低操作系统文件Cache对数据库性能的影响,故调研了一些降低文件系统缓存大小的方法,其中一种是通过修改/proc/sys/vm/dirty_background_r ...
- Jquery实现页面上所有的checkbox只能选中一个
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <hea ...
- 基于css3的文字3D翻转特效
一款基于css3的文字3D翻转特效.这款特效当鼠标经过文字的时候3D翻转显示阴影.效果图如下: 在线预览 源码下载 实现的代码. html代码: <div class="compo ...
- spring jdbctemplate源码跟踪
闲着没事,看看源码也是一种乐趣! java操作数据库的基本步骤都是类似的: 1. 建立数据库连接 2. 创建Connection 3. 创建statement或者preparedStateement ...