iOS CoreData(2)
上面一篇文章介绍了coredata的有关基本概念,由于大部分是参考别人文章中的内容,所以感觉有点虚,而且估计也是比较难以理解,下面这篇文章通俗一点说说学习coredata后的一些理解,然后给出一个简单的demo,有错漏的地方,欢迎读者指正。
其实与coredata有关的有几个概念:
- 数据表 --–> Entity (You usually define entities in a managed object model usingthe data modeling tool in Xcode).
- 表格的记录 --> NSManagedObject (一个表记录就是一个NSManagedObject实例)
- 描述表格结构 --> NSEntityDescription
- 数据库中所有表格和他们的联系 -->NSManagedObjectModel
- 数据库存放方式 --> NSPersistentStoreCoordinator
- 数据库操作 --> NSManagedObjectContext
- 查询语句 --> NSFetchRequest
下面依次讲解一下:
(1)Entity,实体。这个是数据表,也就是你在xcode中可视化编辑的那个东西。它含有Attributes,Relationship和Fetched Property三个属性,其中叫常用的是前两个属性。如下图,就是包含了两个实体(DeviceItem和ImageData),每个实体都含有Attributes,Relationship。
所以可以这样理解,实体就是一个抽象的数据表。
(2)Managed Object,托管对象。是对于Entity实体的模型文件,它有一个基类NSManagedObject,所以的模型文件都是继承NSManagedObject而来。那么我们对应上面说到的两个实体,就可以对应的创建两个模型文件。如下图:
这些模型文件其实就是继承NSManagedObjectd的类,那么以后我们要添加一条记录或者删除一个记录,那么操作的对象就是这些类的实例对象,也就是说一个记录就是一个NSManagedObject实例。
注意到在这些文件中对属性使用的是@dynamic修饰,如在DeviceItem.m文件中
@dynamic iD;
@dynamic imageURL;
@dynamic link;
@dynamic title;
@dynamic image;
那么
@dynamic和@synthesize有什么区别呢?
在声明property属性后,有2种实现选择
@synthesize
编译器期间,让编译器自动生成getter/setter方法。
当有自定义的存或取方法时,自定义会屏蔽自动生成该方法
@dynamic
告诉编译器,不自动生成getter/setter方法,避免编译期间产生警告
然后由自己实现存取方法或存取方法在运行时动态创建绑定:主要使用在CoreData的实现NSManagedObject子类时使用,由Core Data框架在程序运行的时动态生成子类属性
。显然这个就是用来描述实体的,它包含的内容就包括实体的属性,关系等信息,我们通过NSEntityDescription就可以了解实体,那么通过这个NSEntityDescription,我们就可以创建对应实体的Managed Object。所以说,它是用来描述表的结构的。
(4)NSManagedObjectModel,托管对象模型。模型由多个实体描述对象Entity Description构成。通过下图就可以知道什么是托管对象模型了。
(5)NSPersistentStoreCoordinator,持久化存储管理的助理,它是用来负责数据存储的方式的,我们知道coredata的数据存储方式可以有多种SQLite3数据库、二进制文件、XML文件,那么它就是负责这部分内容的。
(6)NSManagedObjectContext,简称是上下文。通过下面这张图应该可以了解上下文的作用了,所以的Managered Object都要在上下文中登记注册,那么你对Managered Object进行操作后,通过上下文就可以进行保存修改,也就说通过上下文进行对数据库数据的以一些操作。
(7)NSFetchRequest ,获取数据的请求,我们要想从上下文中获取到数据,首先还要创建数据请求。创建请求过程中可以指定一些参数,首先是实体,每次获取数据只能指定一种实体,也即指定一种数据表类型,这时候要传入的参数当然就是Entity Description啦;其次还要指定NSPredicate,谓词,也就是限定条件,只有满足条件的数据才被返回;还可以指定排序条件NSSortDescriptor,即按照某个数据项进行升序或者降序的排列。
注意:①其中指定参数中的第一个实体是必须的,而后两个NSPredicate和NSSortDescriptor则是可选的,如果不指定NSPredicate,那么返回的是该实体所描述的所有Managered Objects。
关于如何撰写NSPredicate,这里有详细的介绍:
②获取数据请求返回的数据是以数组的形式,如果指定了排序条件,那么这些数据就会按某个数据项有序返回。
通过下面这种图也可以较形象的理解:
最后再附上两张图片可以对CoreData有一个整体的认识和弄清之间的关系
下面开始讲一下这个小demo。
(1)我们通过建立一个master-detail工程(勾选使用CoreData)那么在AppDelegate中就回生成一些CoreData操作中必须的一些方法和属性。如下:
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; - (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator; //通过context上下文保存coredata中的数据内容
//这个方法的通常是在应用程序退出或者推入后台时调用
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != 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();
}
}
} #pragma mark - Core Data stack // Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
} NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
} // Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"CoreDataDemo" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
} // Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
} NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataDemo.sqlite"]; NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
} #pragma mark - Application's Documents directory // Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
以上这部分代码是通用的,可以直接将这部分的内容转到你的Controller中,这样方法的调用都比较简单;当然你也可以在controller中通过delegate获取到这些方法,进行调用,如下所示(注意要
#import
"AppDelegate.h"
):
AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];
还有一个要说明的是saveContext这个方法的使用,上面的注释中已经有说明使用的情形,例如在AppDelegate中,
- (void)applicationWillTerminate:(UIApplication *)application
{
// Saves changes in the application's managed object context before the application terminates.
[self saveContext];
}
那么假如我们把这些内容转移放到控制器中,那么就可以在添加一个程序推到后台的通知响应,在响应事件的处理中调用这个方法,那么就可以起到保存数据的作用。
说明:最好不要将这些这些coredata操作有关的内容转移到你的controller中,对于小 demo来说是没有问题的,那么如果是对于大一些的工程来说,肯定不是一个controller使用到这些方法,所以还是不做转移好,要使用的地方创建一个AppDelegate实例进行方法调用就好了。
但是下面我讲到的demo中,还是复制了一份到controller中,为的是调用方便。
(2)首先简单介绍这个demo:这个demo会根据一个URL下载一些有关手机的JSON数据,解析后显示在tableview中,而且会保存到coredata中,下一次启动应用程序的时候检测到coredata中有数据就可以不用下载,直接获取数据对tableview进行load数据操作。而且在视图还有一个Fetch按键,用于获取指定的数据内容的操作,然后在终端中显示出来。
该demo中包含两个实体:
下面只显示一些与coredata操作的关键代码,其余部分请下载完整程序代码:下载地址
//初始化上下文
NSManagedObjectContext *context = [self managedObjectContext]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //首先查询coredata中是否存在数据,如果存在数据,那么就不用下载数据了,直接用coredata中保存着的数据就可以了
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"DeviceItem" inManagedObjectContext:context];
//不设置request的predicate,那么将返回coredata中所有的数据
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entityDescription];
NSError *error = nil;
NSArray *itemsArray = [context executeFetchRequest:request error:&error];
//如果获取到的数组元素个数为0,则说明coredata中还没有数据,这时需要下载数据。
if ([itemsArray count] == 0) { NSLog(@"下载数据中....");
NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString:@"http://course.gdou.com/Hw/Files/result.json"] ];
NSError* error = nil;
NSArray *items= [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error != nil)
return;
for(NSDictionary *d in items){
// 创建一个新的Managed Object对象并插入到 context 中(尚未保存到数据库中)
// DeviceItem *di = [NSEntityDescription
// insertNewObjectForEntityForName:@"DeviceItem"
// inManagedObjectContext:context];
// 创建Managed Object 对象的另一种方法:
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"DeviceItem" inManagedObjectContext:context];
DeviceItem* di = [[DeviceItem alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:context]; di.title=[d valueForKey:@"Title"];
di.iD=[d valueForKey:@"ID"];
di.link=[d valueForKey:@"Link"];
di.imageURL=[d valueForKey:@"ImageURL"]; ImageData *md=[NSEntityDescription
insertNewObjectForEntityForName:@"ImageData"
inManagedObjectContext:context];
md.data=[NSData dataWithContentsOfURL:[NSURL URLWithString:di.imageURL]];
// relations
md.item=di;
di.image=md;
// 保存数据模型对象到数据库中
[context save:&error]; [self.allItems addObject:di];
[self.allImages addObject:md];
}
}
//否则,数组中存在元素,那么直接向tableview中load数据即可
else{
NSLog(@"从coredata中获取数据中...");
for (DeviceItem *item in itemsArray) {
//从coredata中获取到的数据初始化两个数组。
[self.allItems addObject:item];
[self.allImages addObject:item.image];
}
}
dispatch_sync(dispatch_get_main_queue(), ^{
block(nil);
});
});
- (IBAction)fetchAction:(id)sender { NSManagedObjectContext *context = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"DeviceItem" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entityDescription]; // 取出iD值以23197 起始的设备.
// for Predicate Format String Syntax, see: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K LIKE[c] %@",@"iD",@"23197?"];
[request setPredicate:predicate];
//按照iD的升序排列
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey:@"iD" ascending:YES];
[request setSortDescriptors:@[sortDescriptor]];
NSError *error = nil;
NSArray *items = [context executeFetchRequest:request error:&error];
//输出iD值以23197 起始的设备的个数。
NSLog(@"count = %d",[items count]);
//依次输出查询到的数据
for (DeviceItem *item in items) {
NSString *idString = item.iD;
NSString *titleString = item.title;
NSLog(@"id : %@,title = %@",idString,titleString);
} }
大致就是以上的内容了,关于如何coredata的具体xcode操作,可以参考这篇文章 http://blog.csdn.net/q199109106q/article/details/8563438
我觉得应该讲清楚了,如有错漏的地方,欢迎读者指正。
iOS CoreData(2)的更多相关文章
- XMPPFrameWork IOS 开发(二)- xcode配置
原始地址:XMPPFrameWork IOS 开发(二) 译文地址: Getting started using XMPPFramework on iOS 介绍 ios上的XMPPFramewor ...
- iOS开发(OC)中的命名规范
开小差:最近发现自己有一个经验主义的毛病,不太容易接受新的知识,这对从事技术研发的人来说不太合理,需要改之. 正文:通过读写大量代码我有自己的一套编程思路和习惯,自认为自己的编码习惯还是不错的,代码结 ...
- XMPPFrameWork IOS 开发(六)聊天室
原始地址:XMPPFrameWork IOS 开发(六)聊天室 聊天室 //初始化聊天室 XMPPJID *roomJID = [XMPPJID jidWithString:ROOM_JID]; xm ...
- XMPPFrameWork IOS 开发(一)xmpp简介
原始地址:XMPPFrameWork IOS 开发(一) XMPP : The Extensible Messaging and Presence Protocol 中文全称: 可扩展通讯和表示协议 ...
- XMPPFrameWork IOS 开发(四)消息和好友上下线
原始地址:XMPPFrameWork IOS 开发(四) 消息 //收到消息 - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XM ...
- XMPPFrameWork IOS 开发(三)登录
原始地址:XMPPFrameWork IOS 开发(三) XMPP中常用对象们: XMPPStream:xmpp基础服务类 XMPPRoster:好友列表类 XMPPRosterCoreDataSto ...
- XE6移动开发环境搭建之IOS篇(7):在Mac OSX 10.8中安装XE6的PAServer(有图有真相)
XE6移动开发环境搭建之IOS篇(7):在Mac OSX 10.8中安装XE6的PAServer(有图有真相) 2014-08-22 21:06 网上能找到的关于Delphi XE系列的移动开发环境的 ...
- XE6移动开发环境搭建之IOS篇(2):安装虚拟机(有图有真相)
XE6移动开发环境搭建之IOS篇(2):安装虚拟机(有图有真相) 2014-08-15 22:04 网上能找到的关于Delphi XE系列的移动开发环境的相关文章甚少,本文尽量以详细的内容.傻瓜式的表 ...
- iOS逆向(五)-ipa包重签名
为什么要重签名? 1.在没有源代码的情况下,你已经对某个应用进行了资源修改(比如修改了启动图或图标等).修改完成以后,如果想要让APP可以正常使用,该APP一定要重新签名然后压缩成IPA文件. 2.如 ...
随机推荐
- codeforces 390C Inna and Candy Boxes
这个题目看似不是很好下手,不过很容易发现每次询问的时候总是会问到第r个盒子是否有糖果: 这样的话就很好办事了: 维护两个数组: 一个sum数组:累加和: 一个in数组:如果i位是1的话,in[i]=i ...
- csuoj 1355: 地雷清除计划
这是一个非常神奇的题: 感觉像一个模拟搜索: 但是竟然可以用网络流来解决: 直接粘题解把: 如果不能走通的话,必然说明能够从右上角(图外面)沿雷“跳” ,一直可以“跳”左下角(图外面) ,因此建好图之 ...
- django的url的name参数的意义(转发)
http://bio.rusaer.com/archives/288 Django一个比较隐含的函数url 阅读量(5010) | 发表 于 2010-03-09 14:26:18 Djang ...
- PHP dirname() 函数
定义和用法 dirname() 函数返回路径中的目录部分. 语法 dirname(path) 参数 描述 path 必需.规定要检查的路径. 说明 path 参数是一个包含有指向一个文件的全路径的字符 ...
- 李洪强iOS开发之-环信04_消息
李洪强iOS开发之-环信04_消息 消息:IM 交互实体,在 SDK 中对应的类型是 EMMessage.EMMessage 由 EMMessageBody 组成. 构造消息 构造文字消息 EMT ...
- Java二维数组
package com.test; public class Test { public static void main(String[] args) { // TODO Auto-generate ...
- 上海CEC大收购(包括华大九天)
紫光收购展讯.锐迪科后,上海开始通过扶植CEC培育新势力,CEC已经收购上海澜起,即将收购amlogic.Ominivision,还在与marvell眉来眼去,此外华大九天已经移植上海,加上之前的上海 ...
- Spring 3.x企业应用开发实战(14)----事务
Spring虽然提供了灵活方便的事务管理功能,但这些功能都是基于底层数据库本身的事务处理机制工作的.要深入了解Spring的事务管理和配置,有必要先对数据库事务的基础知识进行学习. 何为数据库事务 “ ...
- C#程序中访问配置文件
在C#编程中,有时候会用到配置文件,那么该如何在程序中获取或修改配置文件中的相关数据呢?下面采用一个简单的C#控制台程序来说明. 新建一个C#控制台程序,打开“解决方案资源管理器”,如下图: 可以看到 ...
- C#开发漂亮的数字时钟
今天用C#做了一个漂亮的数字时钟.界面如下. 实现技术:主要是通过Graphics类的DrawImage方法来绘制数字时钟中所有的数字,这些数字是从网上找的一些图片文件.时钟使用DateTime中No ...