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 ...
随机推荐
- codeforces Gym 100187L L. Ministry of Truth 水题
L. Ministry of Truth Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100187/p ...
- poj 1845(等比数列前n项和及高速幂)
Sumdiv Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 13959 Accepted: 3433 Descripti ...
- window.showModalDialog 与window.open传递参数的不同?
简单的说,就是一个在弹出窗口之后可以做其它的事,即window.open 另一个在弹出窗口之后不能做其它的事,只能是关闭了当前的窗口之后才能做其它的事,即window.showModalDialog ...
- 4K加速普及,8K近在咫尺,下一个是?
从铺天盖地的厂商宣传到亲戚朋友家的客厅.不可否认4K时代已全面到来------业内人士估计2015年是4K电视的一个突破年.知名市场调查机构StrategyAnalytics对4K电视进行了调 ...
- mkinitrd---简单介绍
转载:http://blog.csdn.net/zwcq82/article/details/4295481 原来对mkinitrd不是很了解.最近做内核升级,需要制作信息的initrd文件,发现出错 ...
- SQL Server 的事务和锁(一)
最近在项目中进行压力测试遇到了数据库的死锁问题,简言之,如下的代码在 SERIALIZABLE 隔离级别造成了死锁: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 SELECT @ ...
- iOS 抓取 UIwebview 上 所有 图片 并进行滚动播放
关于在UIwebview上添加滚动图片 两种滚动手势会混淆,应为webview有webview.scrollview的属性 故参照昨天的随笔 scrollview嵌套解决方案. 本篇随笔主要讲循环使用 ...
- Ajax的工作原理
Ajax的核心是JavaScript对象XmlHttpRequest.该对象在Internet Explorer 5中首次引入,它是一种支持异步请求的技术.简而言之,XmlHttpRequest使您可 ...
- TCP/IP协议原理与应用笔记27:网际协议(IP)之 选项(Options)
1. 选项(Options) (1)作用:网络测试或者调试,可选 (2)格式:0~40 bytes 2. 选项类型:
- Android学习笔记⑥——UI组件的学习ImageView相关
ImageView是集成了View的组件,它的主要工作就是显示一些图片啊,虽然他的用法一句话概括了,但是我觉得学起来应该不会太简单,正所谓 短小而精悍么 :) ImageView 派生了 ImageB ...